terminar - llamar una funcion javascript con jquery
Llamar sincrónicamente a una función Javascript asíncrona (5)
En primer lugar, este es un caso muy específico de hacerlo incorrectamente a propósito para adaptar una llamada asincrónica en una base de código muy sincrónica que tiene muchos miles de líneas y el tiempo no permite actualmente la posibilidad de realizar los cambios para "hacer bien." Duele cada fibra de mi ser, pero la realidad y los ideales a menudo no encajan. Sé que esto apesta.
OK, eso fuera del camino, cómo lo hago para poder:
function doSomething() {
var data;
function callBack(d) {
data = d;
}
myAsynchronousCall(param1, callBack);
// block here and return data when the callback is finished
return data;
}
Los ejemplos (o la falta de ellos) usan bibliotecas y / o compiladores, los cuales no son viables para esta solución. Necesito un ejemplo concreto de cómo bloquearlo (por ejemplo, NO dejar la función doSomething hasta que se llame) sin congelar la IU. Si tal cosa es posible en JS.
"No me digas cómo debería hacerlo" de la manera correcta "o lo que sea"
DE ACUERDO. pero deberías hacerlo de la manera correcta ... o lo que sea
"Necesito un ejemplo concreto de cómo bloquearlo ... SIN congelar la UI. Si eso 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, luego haga que la devolución de llamada establezca los data
en el 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 puede modificar doSomething()
. No sé si eso está en las cartas.
Si se puede modificar, entonces no sé por qué no pasaría simplemente una devolución de llamada a doSomething()
para que se llame desde la otra devolución de llamada, pero es mejor que me detenga antes de tener problemas. ;)
Oh, qué diablos. Usted dio 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);
});
Como su ejemplo incluye una devolución de llamada que se pasa a la llamada asincrónica, la forma correcta sería pasar una función a hacer doSomething()
que se invocará desde la devolución de llamada.
Por supuesto, si eso es lo único que hace la devolución de llamada, simplemente pasarás func
directamente ...
myAsynchronousCall(param1, func);
Eche un vistazo a JQuery Promises:
http://api.jquery.com/promise/
http://api.jquery.com/jQuery.when/
http://api.jquery.com/deferred.promise/
Refactorizar 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);
Hay una buena solución en http://taskjs.org/
Utiliza generadores que son nuevos en javascript. Por lo tanto, actualmente no está implementado por la mayoría de los navegadores. Lo probé en Firefox, y para mí es una buena manera de ajustar la función asincrónica.
Aquí está el código de ejemplo del proyecto GitHub
var { Deferred } = task;
spawn(function() {
out.innerHTML = "reading.../n";
try {
var d = yield read("read.html");
alert(d.responseText.length);
} catch (e) {
e.stack.split(//n/).forEach(function(line) { console.log(line) });
console.log("");
out.innerHTML = "error: " + e;
}
});
function read(url, method) {
method = method || "GET";
var xhr = new XMLHttpRequest();
var deferred = new Deferred();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >= 400) {
var e = new Error(xhr.statusText);
e.status = xhr.status;
deferred.reject(e);
} else {
deferred.resolve({
responseText: xhr.responseText
});
}
}
};
xhr.open(method, url, true);
xhr.send();
return deferred.promise;
}
Puede usar recursión, simplemente cree una función que se llame a sí misma, llame a esta función 1 vez desde afuera, dentro de la función recursiva llame a su código asíncrono, repita su función recursiva mediante el uso de devoluciones de llamada. 🤗
Las funciones Async , una característica de ES2017 , hacen que el código asincrónico parezca sincronizado mediante el uso de promises (una forma particular de código asincrónico) y la palabra clave await
. Observe también la palabra clave async
delante de la palabra clave de function
que significa una función async / await. La palabra clave await no funcionará sin ella. Eso significa que no hay nivel superior esperando.
ES2017 fue ratificado (es decir, finalizado) el 27 de junio de 2017. Puede que Async ya funcione en su navegador, pero si no puede seguir utilizando la funcionalidad con un transpiler de javascript como babel o traceur . Chrome 55 es totalmente compatible con las funciones asincrónicas. Entonces, si tiene un navegador más nuevo, puede probar el siguiente código.
Vea la tabla de compatibilidad es2017 de kangax para la compatibilidad del navegador.
Aquí hay un ejemplo de la función de espera de doAsync
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 de función, pero no detendrá ninguna otra función y continuará al ejecutar otro código hasta que la promesa se resuelva, desplegará el valor de la promesa y se puede pensar en la expresión de espera y promesa como reemplazada por ese valor desenvuelto. Entonces, dado que espera solo las pausas espera y luego desenvuelve un valor antes de ejecutar el resto de la línea, puede usarlo para los bucles y dentro de las llamadas de función como en el ejemplo siguiente que recoge 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í devuelve una promesa para que pueda usar eso como una promesa con el encadenamiento como lo hago arriba o dentro de otra función de espera asincrónica.
La función anterior esperaría cada respuesta antes de enviar otra solicitud. Si desea enviar las solicitudes al mismo tiempo, puede llamar a su uso asincrónico 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 rechaza, puede envolverla en una captura de prueba u omitir la captura de prueba y permitir que el error se propague a la llamada catch de funciones async / await. Debe tener cuidado de no dejar los errores de promesa no administrados 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 ve aquí , puede ver las propuestas finalizadas para las futuras versiones de ECMAScript.
Una alternativa a esto que se puede usar con solo ES2015 (ES6) es usar una función especial que envuelve una función de generador. Las funciones del generador tienen una palabra clave de rendimiento que se puede usar para replicar la palabra clave await con una función circundante. La palabra clave de rendimiento y la función del generador son mucho más generales y pueden hacer muchas más cosas que lo que hace la función async await. Si quieres una envoltura de funciones del generador que se pueda usar para replicar la función de sincronización aguarda, verificaría co.js Por cierto, la función de co, al igual que las funciones de espera asíncronas, devuelve una promesa.
La compatibilidad con el navegador es bastante buena ahora (2017) en todos los principales navegadores actuales (Chrome, Safari y Edge) excepto IE.