Découvrez ceci 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");
});
Comme vous pouvez le constater dans la console, la fonction "animate" est asynchrone et "branche" le flux du code de bloc du gestionnaire d'événements. En réalité :
$('#link').click(function () {
console.log("Enter");
asyncFunct();
console.log("Exit");
});
function asyncFunct() {
console.log("finished");
}
suivez le flux du code de bloc!
Si je souhaite créer ma function asyncFunct() { }
avec ce comportement, comment puis-je le faire avec javascript/jquery? Je pense qu'il existe une stratégie sans l'utilisation de setTimeout()
Vous ne pouvez pas créer une fonction asynchrone vraiment personnalisée. Vous devrez éventuellement utiliser une technologie fournie nativement, telle que:
setInterval
setTimeout
requestAnimationFrame
XMLHttpRequest
WebSocket
Worker
onload
En fait, pour l'animation jQuery tilisesetInterval
.
Vous pouvez utiliser une minuterie:
setTimeout( yourFn, 0 );
(où yourFn
est une référence à votre fonction)
ou, avec Lodash :
_.defer( yourFn );
Diffère l'appel de la
func
jusqu'à ce que la pile d'appels en cours soit effacée. Tous les arguments supplémentaires sont fournis àfunc
lors de son invocation.
ici vous avez une solution simple (autre écrit à ce sujet) http://www.benlesh.com/2012/05/calling-javascript-function.html
Et voici la solution ci-dessus:
function async(your_function, callback) {
setTimeout(function() {
your_function();
if (callback) {callback();}
}, 0);
}
TEST 1 ( peut indiquer '1 x 2 3' ou '1 2 x 3' ou '1 2 3 x' ):
console.log(1);
async(function() {console.log('x')}, null);
console.log(2);
console.log(3);
TEST 2 ( affichera toujours 'x 1' ):
async(function() {console.log('x');}, function() {console.log(1);});
Cette fonction est exécutée avec timeout 0 - elle simule une tâche asynchrone
Voici une fonction qui prend une autre fonction et génère une version asynchrone.
var async = function (func) {
return function () {
var args = arguments;
setTimeout(function () {
func.apply(this, args);
}, 0);
};
};
Il est utilisé comme un moyen simple de créer une fonction asynchrone:
var anyncFunction = async(function (callback) {
doSomething();
callback();
});
Ceci diffère de la réponse de @ fider car la fonction elle-même a sa propre structure (aucun rappel n'a été ajouté, elle est déjà dans la fonction) et également parce qu'elle crée une nouvelle fonction pouvant être utilisée.
Edit: J'ai totalement mal compris la question. Dans le navigateur, j'utiliserais setTimeout
. S'il était important qu'il s'exécute dans un autre thread, j'utiliserais Web Workers .
Cette page vous explique les bases de la création d’une fonction javascript async.
Depuis ES2017, les fonctions javacript asynchrones sont beaucoup plus faciles à écrire. Vous devriez également en savoir plus sur Promises .
En retard, mais pour montrer une solution facile en utilisant promises
après leur introduction dans ES6 , il gère beaucoup plus facilement les appels asynchrones:
Vous définissez le code asynchrone dans une nouvelle promesse:
var asyncFunct = new Promise(function(resolve, reject) {
$('#link').animate({ width: 200 }, 2000, function() {
console.log("finished");
resolve();
});
});
Remarque pour définir resolve()
à la fin de l'appel asynchrone.
Ensuite, vous ajoutez le code que vous souhaitez exécuter après la fin de l'appel asynchrone dans .then()
de la promesse:
asyncFunct.then((result) => {
console.log("Exit");
});
En voici un extrait:
$('#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>
ou JSFiddle
Si vous souhaitez utiliser les paramètres et régler le nombre maximal de fonctions asynchrones, vous pouvez utiliser un outil de traitement async simple que j'ai construit:
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);
}
}
Vous pouvez l'utiliser comme ceci:
var myFunction = function() {
...
}
var myFunctionB = function() {
...
}
var myParams = { name: "John" };
var bgworker = new BackgroundWorker();
bgworker.runTask(myFunction, 0, myParams);
bgworker.runTask(myFunctionB, 0, null);
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);
Bien que n'étant pas fondamentalement différente des autres solutions, je pense que ma solution apporte quelques améliorations supplémentaires:
Function.prototype
permettant ainsi de l'appeler plus facilementDe plus, la similarité avec la fonction intégrée Function.prototype.apply
me semble appropriée.
En plus de l'excellente réponse de @pimvdb, et juste au cas où vous vous le demanderiez, async.js n'offre pas non plus de fonctions véritablement asynchrones. Voici une version (très) simplifiée de la méthode principale de la bibliothèque:
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);
};
}
Donc, la fonction protège des erreurs et la remet gracieusement au callback, mais le code est aussi synchrone que toute autre fonction JS.
Malheureusement, JavaScript ne fournit pas de fonctionnalité asynchrone. Cela ne fonctionne que dans un seul thread. Mais la plupart des navigateurs modernes fournissent Worker
s , qui sont des seconds scripts exécutés en arrière-plan et pouvant renvoyer un résultat. J'ai donc trouvé une solution qui, selon moi, est utile pour exécuter une fonction de manière asynchrone, ce qui crée un opérateur pour chaque appel asynchrone.
Le code ci-dessous contient la fonctionasync
appeler en arrière-plan.
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);
};
Ceci est un exemple d'utilisation:
(function(x) {
for (let i = 0; i < 999999999; i++) {}
return x * 2;
}).async(function(result) {
alert(result);
})(10);
Ceci exécute une fonction qui itère une for
avec un nombre énorme pour prendre du temps en tant que démonstration d'asynchronicité, puis obtient le double du nombre passé. La méthode async
fournit une function
qui appelle la fonction voulue en arrière-plan et qui fournit le paramètre de async
rappelle la return
dans son paramètre unique. Donc, dans la fonction de rappel I alert
le résultat.
MDN a un bon exemple sur l'utilisation de setTimeout en préservant "this".
Comme ce qui suit:
function doSomething() {
// use 'this' to handle the selected element here
}
$(".someSelector").each(function() {
setTimeout(doSomething.bind(this), 0);
});