Saltar al contenido

Llamar a una función de JavaScript asincrónica de forma sincrónica

Solución:

“no me digas cómo debería hacerlo” de la manera correcta “o lo que sea”

está bien. pero realmente deberías hacerlo de la manera correcta … o lo que sea

“Necesito un ejemplo concreto de cómo hacer que se bloquee … SIN congelar la interfaz de usuario. Si tal cosa es posible en JS”.

No, es imposible bloquear el JavaScript en ejecución sin bloquear la interfaz de usuario.

Dada la falta de información, es difícil ofrecer una solución, pero una opción puede ser hacer que la función de llamada realice un sondeo para verificar una variable global, y luego configurar la devolución de llamada. data a lo global.

function doSomething() {

      // callback sets the received data to a global var
  function callBack(d) {
      window.data = d;
  }
      // start the async
  myAsynchronousCall(param1, callBack);

}

  // start the function
doSomething();

  // make sure the global is clear
window.data = null

  // start polling at an interval until the data is found at the global
var intvl = setInterval(function() {
    if (window.data) { 
        clearInterval(intvl);
        console.log(data);
    }
}, 100);

Todo esto supone que puedes modificar doSomething(). No sé si eso está en las cartas.

Si se puede modificar, entonces no sé por qué no pasaría una devolución de llamada a doSomething() ser llamado desde la otra devolución de llamada, pero mejor me detengo antes de meterme en problemas. 😉


Oh, que diablos. Diste un ejemplo que sugiere que se puede hacer correctamente, así que voy a mostrar esa solución …

function doSomething( func ) {

  function callBack(d) {
    func( d );
  }

  myAsynchronousCall(param1, callBack);

}

doSomething(function(data) {
    console.log(data);
});

Debido a que su ejemplo incluye una devolución de llamada que se pasa a la llamada asíncrona, la forma correcta sería pasar una función a doSomething() para ser invocado desde la devolución de llamada.

Por supuesto, si eso es lo único que hace la devolución de llamada, simplemente pasaría func directamente…

myAsynchronousCall(param1, func);

Las funciones asíncronas, una característica de ES2017, hacen que el código asíncrono parezca sincronizado mediante el uso de promesas (una forma particular de código asíncrono) y la await palabra clave. También observe en los ejemplos de código debajo de la palabra clave async en frente de function palabra clave que significa una función asíncrona / en espera. los await la palabra clave no funcionará sin estar en una función prefijada con el async palabra clave. Dado que actualmente no hay una excepción a esto, eso significa que no funcionarán las esperas de nivel superior (las esperas de nivel superior significan una espera fuera de cualquier función). Aunque existe una propuesta de nivel superior await.

ES2017 fue ratificado (es decir, finalizado) como estándar para JavaScript el 27 de junio de 2017. Es posible que Async await ya funcione en su navegador, pero si no, aún puede usar la funcionalidad usando un transpilador de JavaScript como babel o traceur. Chrome 55 tiene soporte completo para funciones asíncronas. Entonces, si tiene un navegador más nuevo, puede probar el código a continuación.

Consulte la tabla de compatibilidad de kangax es2017 para conocer la compatibilidad del navegador.

Aquí hay un ejemplo de función de espera asíncrona llamada doAsync que toma tres pausas de un segundo e imprime la diferencia de tiempo después de cada pausa desde la hora de inicio:

function timeoutPromise (time) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve(Date.now());
    }, time)
  })
}

function doSomethingAsync () {
  return timeoutPromise(1000);
}

async function doAsync () {
  var start = Date.now(), time;
  console.log(0);
  time = await doSomethingAsync();
  console.log(time - start);
  time = await doSomethingAsync();
  console.log(time - start);
  time = await doSomethingAsync();
  console.log(time - start);
}

doAsync();

Cuando la palabra clave await se coloca antes de un valor de promesa (en este caso, el valor de promesa es el valor devuelto por la función doSomethingAsync), la palabra clave await pausará la ejecución de la llamada a la función, pero no pausará ninguna otra función y continuará. ejecutando otro código hasta que se resuelva la promesa. Después de que la promesa se resuelva, desenvolverá el valor de la promesa y puede pensar que la expresión de espera y promesa ahora está siendo reemplazada por ese valor desenvuelto.

Entonces, dado que await solo hace una pausa, espera y luego desenvuelve un valor antes de ejecutar el resto de la línea, puede usarlo en bucles for y llamadas a funciones internas como en el siguiente ejemplo, que recopila las diferencias de tiempo esperadas en una matriz e imprime la matriz.

function timeoutPromise (time) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve(Date.now());
    }, time)
  })
}

function doSomethingAsync () {
  return timeoutPromise(1000);
}

// this calls each promise returning function one after the other
async function doAsync () {
  var response = [];
  var start = Date.now();
  // each index is a promise returning function
  var promiseFuncs= [doSomethingAsync, doSomethingAsync, doSomethingAsync];
  for(var i = 0; i < promiseFuncs.length; ++i) {
    var promiseFunc = promiseFuncs[i];
    response.push(await promiseFunc() - start);
    console.log(response);
  }
  // do something with response which is an array of values that were from resolved promises.
  return response
}

doAsync().then(function (response) {
  console.log(response)
})

La función asincrónica en sí misma devuelve una promesa, por lo que puede usarla como una promesa con el encadenamiento como lo hago anteriormente o dentro de otra función de espera asíncrona.

La función anterior esperaría cada respuesta antes de enviar otra solicitud. Si desea enviar las solicitudes al mismo tiempo, puede usar Promise.all.

// no change
function timeoutPromise (time) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve(Date.now());
    }, time)
  })
}

// no change
function doSomethingAsync () {
  return timeoutPromise(1000);
}

// this function calls the async promise returning functions all at around the same time
async function doAsync () {
  var start = Date.now();
  // we are now using promise all to await all promises to settle
  var responses = await Promise.all([doSomethingAsync(), doSomethingAsync(), doSomethingAsync()]);
  return responses.map(x=>x-start);
}

// no change
doAsync().then(function (response) {
  console.log(response)
})

Si la promesa posiblemente se rechaza, puede envolverla en un try catch u omitir try catch y dejar que el error se propague a la llamada de captura de funciones async / await. Debe tener cuidado de no dejar errores de promesa sin controlar, especialmente en Node.js. A continuación se muestran algunos ejemplos que muestran cómo funcionan los errores.

function timeoutReject (time) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      reject(new Error("OOPS well you got an error at TIMESTAMP: " + Date.now()));
    }, time)
  })
}

function doErrorAsync () {
  return timeoutReject(1000);
}

var log = (...args)=>console.log(...args);
var logErr = (...args)=>console.error(...args);

async function unpropogatedError () {
  // promise is not awaited or returned so it does not propogate the error
  doErrorAsync();
  return "finished unpropogatedError successfully";
}

unpropogatedError().then(log).catch(logErr)

async function handledError () {
  var start = Date.now();
  try {
    console.log((await doErrorAsync()) - start);
    console.log("past error");
  } catch (e) {
    console.log("in catch we handled the error");
  }
  
  return "finished handledError successfully";
}

handledError().then(log).catch(logErr)

// example of how error propogates to chained catch method
async function propogatedError () {
  var start = Date.now();
  var time = await doErrorAsync() - start;
  console.log(time - start);
  return "finished propogatedError successfully";
}

// this is what prints propogatedError's error.
propogatedError().then(log).catch(logErr)

Si va aquí, puede ver las propuestas terminadas para las próximas versiones de ECMAScript.

Una alternativa a esto que se puede usar solo con ES2015 (ES6) es usar una función especial que envuelve una función de generador. Las funciones generadoras tienen una palabra clave de rendimiento que puede usarse para replicar la palabra clave await con una función circundante. La palabra clave yield y la función generadora tienen un propósito mucho más general y pueden hacer muchas más cosas que las que hace la función async await. Si desea un contenedor de funciones de generador que se pueda usar para replicar async await, echaría un vistazo a co.js. Por cierto, la función de co, al igual que las funciones async en espera, devuelven una promesa. Honestamente, aunque en este punto, la compatibilidad del navegador es casi la misma para las funciones del generador y las funciones asíncronas, por lo que si solo desea la funcionalidad de espera asíncrona, debe usar las funciones asíncronas sin co.js.

El soporte del navegador es bastante bueno ahora para las funciones Async (a partir de 2017) en todos los principales navegadores actuales (Chrome, Safari y Edge) excepto IE.

Eche un vistazo a JQuery Promises:

http://api.jquery.com/promise/

http://api.jquery.com/jQuery.when/

http://api.jquery.com/deferred.promise/

Refactorice el código:

    var dfd = new jQuery.Deferred();


    function callBack(data) {
       dfd.notify(data);
    }

    // do the async call.
    myAsynchronousCall(param1, callBack);

    function doSomething(data) {
     // do stuff with data...
    }

    $.when(dfd).then(doSomething);


¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *