tipos - tablas dinamicas html javascript
¿Cómo puedo crear una función asincrónica en Javascript? (13)
Quiero decir, mira este code :
<a href="#" id="link">Link</a>
<span>Moving</span>
$(''#link'').click(function () {
console.log("Enter");
$(''#link'').animate({ width: 200 }, 2000, function() {
console.log("finished");
});
console.log("Exit");
});
como se puede ver en la consola, la función "animar" es Asíncrona, y "bifurca" el flujo del código de bloqueo del controlador de eventos. De hecho :
$(''#link'').click(function () {
console.log("Enter");
asyncFunct();
console.log("Exit");
});
function asyncFunct() {
console.log("finished");
}
sigue el flujo del código de bloque!
Si deseo crear mi function asyncFunct() { }
con este comportamiento, ¿cómo puedo hacerlo con javascript / jquery? Creo que hay una estrategia sin uso setTimeout()
Aquí hay una función que adopta otra función y genera una versión que se ejecuta de manera asíncrona.
var async = function (func) {
return function () {
var args = arguments;
setTimeout(function () {
func.apply(this, args);
}, 0);
};
};
Se usa como una forma simple de hacer una función asíncrona:
var anyncFunction = async(function (callback) {
doSomething();
callback();
});
Esto es diferente de la respuesta de @ fider porque la función en sí tiene su propia estructura (no se ha agregado una devolución de llamada, ya está en la función) y también porque crea una nueva función que se puede usar.
Desafortunadamente, JavaScript no proporciona una funcionalidad asincrónica. Funciona solo en un único hilo. Pero la mayoría de los navegadores modernos proporcionan Worker
s , que son segundos scripts que se ejecutan en segundo plano y pueden devolver un resultado. Entonces, llegué a una solución. Creo que es útil ejecutar de forma asíncrona una función, que crea un trabajador para cada llamada asíncrona.
El siguiente código contiene la función async
para llamar al fondo.
Function.prototype.async = function(callback) {
let blob = new Blob([ "self.addEventListener(''message'', function(e) { self.postMessage({ result: (" + this + ").apply(null, e.data) }); }, false);" ], { type: "text/javascript" });
let worker = new Worker(window.URL.createObjectURL(blob));
worker.addEventListener("message", function(e) {
this(e.data.result);
}.bind(callback), false);
return function() {
this.postMessage(Array.from(arguments));
}.bind(worker);
};
Este es un ejemplo de uso:
(function(x) {
for (let i = 0; i < 999999999; i++) {}
return x * 2;
}).async(function(result) {
alert(result);
})(10);
Esto ejecuta una función que itera un for
con un número enorme para tomar el tiempo como demostración de asincronicidad, y luego obtiene el doble del número pasado. El método async
proporciona una function
que llama a la función deseada en segundo plano, y en la que se proporciona como parámetro de devoluciones de llamada async
la return
en su parámetro único. Entonces, en la función de devolución de llamada, alert
el resultado.
Junto a la gran respuesta de @pimvdb, y por si acaso te preguntas, async.js tampoco ofrece funciones realmente asíncronas. Aquí hay una versión (muy) simplificada del método principal de la biblioteca:
function asyncify(func) { // signature: func(array)
return function (array, callback) {
var result;
try {
result = func.apply(this, array);
} catch (e) {
return callback(e);
}
/* code ommited in case func returns a promise */
callback(null, result);
};
}
Por lo tanto, la función protege de los errores y la transfiere elegantemente a la devolución de llamada para manejarla, pero el código es tan sincrónico como cualquier otra función de JS.
MDN tiene un buen ejemplo sobre el uso de setTimeout preservando "esto".
Como el siguiente:
function doSomething() {
// use ''this'' to handle the selected element here
}
$(".someSelector").each(function() {
setTimeout(doSomething.bind(this), 0);
});
No se puede hacer una función verdaderamente asincrónica personalizada. Eventualmente tendrá que aprovechar una tecnología proporcionada de forma nativa, como:
-
setInterval
-
setTimeout
-
requestAnimationFrame
-
XMLHttpRequest
-
WebSocket
-
Worker
- Algunas API HTML5, como la API de archivos, la API de la base de datos web
- Tecnologías que soportan
onload
- ... muchos otros
De hecho, para la animación jQuery uses setInterval
.
Puedes usar un temporizador:
setTimeout( yourFn, 0 );
(donde yourFn
es una referencia a su función)
o, con Lodash :
_.defer( yourFn );
Defiere invocar el
func
hasta que sefunc
la pila de llamadas actual. Cualquier argumento adicional se proporciona afunc
cuando se invoca.
Si desea usar Parámetros y regular la cantidad máxima de funciones asíncronas, puede usar un trabajador asincrónico simple que he compilado:
var BackgroundWorker = function(maxTasks) {
this.maxTasks = maxTasks || 100;
this.runningTasks = 0;
this.taskQueue = [];
};
/* runs an async task */
BackgroundWorker.prototype.runTask = function(task, delay, params) {
var self = this;
if(self.runningTasks >= self.maxTasks) {
self.taskQueue.push({ task: task, delay: delay, params: params});
} else {
self.runningTasks += 1;
var runnable = function(params) {
try {
task(params);
} catch(err) {
console.log(err);
}
self.taskCompleted();
}
// this approach uses current standards:
setTimeout(runnable, delay, params);
}
}
BackgroundWorker.prototype.taskCompleted = function() {
this.runningTasks -= 1;
// are any tasks waiting in queue?
if(this.taskQueue.length > 0) {
// it seems so! let''s run it x)
var taskInfo = this.taskQueue.splice(0, 1)[0];
this.runTask(taskInfo.task, taskInfo.delay, taskInfo.params);
}
}
Puedes usarlo así:
var myFunction = function() {
...
}
var myFunctionB = function() {
...
}
var myParams = { name: "John" };
var bgworker = new BackgroundWorker();
bgworker.runTask(myFunction, 0, myParams);
bgworker.runTask(myFunctionB, 0, null);
Tarde, pero para mostrar una solución fácil utilizando promises
después de su introducción en ES6 , maneja llamadas asincrónicas mucho más fácil:
Establece el código asincrónico en una nueva promesa:
var asyncFunct = new Promise(function(resolve, reject) {
$(''#link'').animate({ width: 200 }, 2000, function() {
console.log("finished");
resolve();
});
});
Nota para establecer la resolve()
cuando finaliza la llamada asincrónica.
A continuación, agregue el código que desea ejecutar después de que finalice la llamada asincrónica dentro de .then()
de la promesa:
asyncFunct.then((result) => {
console.log("Exit");
});
Aquí hay un fragmento de esto:
$(''#link'').click(function () {
console.log("Enter");
var asyncFunct = new Promise(function(resolve, reject) {
$(''#link'').animate({ width: 200 }, 2000, function() {
console.log("finished");
resolve();
});
});
asyncFunct.then((result) => {
console.log("Exit");
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#" id="link">Link</a>
<span>Moving</span>
o JSFiddle
Veo aquí que nadie habló sobre la promesa. Así que te recomiendo que aprendas acerca de la promesa: mira este enlace
aquí tienes una solución simple (otra escribe sobre ella) http://www.benlesh.com/2012/05/calling-javascript-function.html
Y aquí tienes una solución lista para arriba:
function async(your_function, callback) {
setTimeout(function() {
your_function();
if (callback) {callback();}
}, 0);
}
PRUEBA 1 ( puede dar salida ''1 x 2 3'' o ''1 2 x 3'' o ''1 2 3 x'' ):
console.log(1);
async(function() {console.log(''x'')}, null);
console.log(2);
console.log(3);
PRUEBA 2 ( siempre arrojará ''x 1'' ):
async(function() {console.log(''x'');}, function() {console.log(1);});
Esta función se ejecuta con timeout 0 - simulará una tarea asíncrona
Esta página lo guiará a través de los pasos básicos para crear una función asíncrona de JavaScript.
La realización de llamadas a funciones asíncronas en JavaScript utilizando argumentos normalmente implica la construcción manual del argumento de expresión para setTimeout o setInterval.
Si eso no lo resuelve, consulte la documentación de la función animate
.
Editar: He entendido mal la pregunta. En el navegador, usaría setTimeout
. Si era importante que funcionara en otro hilo, usaría Web Workers .
Function.prototype.applyAsync = function(params, cb){
var function_context = this;
setTimeout(function(){
var val = function_context.apply(undefined, params);
if(cb) cb(val);
}, 0);
}
// usage
var double = function(n){return 2*n;};
var display = function(){console.log(arguments); return undefined;};
double.applyAsync([3], display);
Aunque no es fundamentalmente diferente de las otras soluciones, creo que mi solución hace algunas cosas agradables adicionales:
- permite parámetros para las funciones
- pasa la salida de la función a la devolución de llamada
- se agrega a
Function.prototype
permitiendo una mejor forma de llamarlo
Además, la similitud con la función incorporada Function.prototype.apply
parece adecuada.