Fonctions asynchrones une fonction dans ES2017 pour faire en sorte que le code asynchrone ait l'air synchrone en utilisant promesses (une forme particulière de code asynchrone) et l'option await
mot-clé. Remarquez également dans les exemples de code ci-dessous le mot-clé async
devant le function
qui signifie une fonction async/await. L'adresse await
ne fonctionnera pas s'il ne se trouve pas dans une fonction préfixée par le mot-clé async
mot-clé. Comme il n'y a actuellement aucune exception à cette règle, cela signifie qu'aucune attente de niveau supérieur ne fonctionnera (une attente de niveau supérieur signifie une attente en dehors de toute fonction). Bien qu'il existe un proposition de niveau supérieur await
.
L'ES2017 a été ratifiée (c'est-à-dire finalisée) comme norme pour JavaScript le 27 juin 2017. Async await fonctionne peut-être déjà dans votre navigateur, mais si ce n'est pas le cas, vous pouvez toujours utiliser la fonctionnalité en utilisant un transpilateur javascript comme babel o traceur . Chrome 55 a un support complet des fonctions asynchrones. Donc si vous avez un navigateur plus récent, vous pouvez essayer le code ci-dessous.
Voir table de compatibilité es2017 de kangax pour la compatibilité avec les navigateurs.
Voici un exemple de fonction async await appelée doAsync
qui prend trois pauses d'une seconde et imprime la différence de temps après chaque pause par rapport au temps de départ :
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();
Lorsque le mot-clé await est placé devant une valeur de promesse (dans ce cas, la valeur de la promesse est la valeur renvoyée par la fonction doSomethingAsync), le mot-clé await met en pause l'exécution de l'appel de fonction, mais il ne met en pause aucune autre fonction et continue à exécuter d'autres codes jusqu'à ce que la promesse soit résolue. Une fois la promesse résolue, la valeur de la promesse sera déballée et vous pouvez considérer que les expressions await et promise sont maintenant remplacées par cette valeur déballée.
Ainsi, puisque await se contente de mettre en pause, d'attendre puis de déballer une valeur avant d'exécuter le reste de la ligne, vous pouvez l'utiliser dans des boucles for et à l'intérieur d'appels de fonction comme dans l'exemple ci-dessous qui collecte les différences de temps attendues dans un tableau et imprime le tableau.
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 fonction asynchrone elle-même renvoie une promesse, vous pouvez donc l'utiliser comme une promesse avec un chaînage comme je le fais ci-dessus ou dans une autre fonction asynchrone await.
La fonction ci-dessus attendrait chaque réponse avant d'envoyer une autre requête ; si vous souhaitez envoyer les requêtes simultanément, vous pouvez utiliser 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 promesse est éventuellement rejetée, vous pouvez l'envelopper dans un try catch ou sauter le try catch et laisser l'erreur se propager jusqu'à l'appel catch des fonctions async/await. Vous devez faire attention à ne pas laisser les erreurs de promesse non gérées, en particulier dans Node.js. Voici quelques exemples qui illustrent le fonctionnement des erreurs.
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 vous allez aquí vous pouvez voir les propositions finalisées pour les prochaines versions d'ECMAScript.
Une alternative à cela, qui peut être utilisée uniquement avec ES2015 (ES6), consiste à utiliser une fonction spéciale qui enveloppe une fonction de générateur. Les fonctions génératrices ont un mot-clé yield qui peut être utilisé pour reproduire le mot-clé await avec une fonction environnante. Le mot-clé yield et la fonction de générateur sont beaucoup plus généraux et peuvent faire beaucoup plus que ce que fait la fonction async await. Si vous voulez une enveloppe de fonction de générateur qui peut être utilisée pour répliquer la fonction async await, je vous conseille de consulter les sites suivants co.js . Au fait, les fonctions de co, tout comme les fonctions async await, renvoient une promesse. Honnêtement, à ce stade, la compatibilité avec les navigateurs est à peu près la même pour les fonctions génératrices et les fonctions asynchrones, donc si vous voulez seulement la fonctionnalité async await, vous devriez utiliser les fonctions asynchrones sans co.js.
La prise en charge des navigateurs est en fait assez bonne maintenant pour les fonctions asynchrones (depuis 2017) dans tous les principaux navigateurs actuels (Chrome, Safari et Edge) sauf IE.
17 votes
Il n'est tout simplement pas possible de bloquer un navigateur et d'attendre. Ils ne le feront tout simplement pas.
2 votes
Javascript n'a pas de mécanisme de blocage sur la plupart des navigateurs... vous devrez créer un callback qui sera appelé à la fin de l'appel asynchrone pour retourner les données.
0 votes
Je ne pense pas que cela puisse être fait. Javascript (malgré les web workers) est monofilaire - le callback ne peut pas être appelé avant le retour du gestionnaire d'événement actuel. Cela nécessiterait l'équivalent de la fonction async/await de C# en Javascript et je doute qu'une telle bête existe. Je crains que vous ne soyez obligé de réécrire votre code en CPS, aussi ennuyeux que cela puisse être.
9 votes
Vous demandez un moyen de dire au navigateur "Je sais que je viens de te dire d'exécuter cette fonction précédente de manière asynchrone, mais je ne le pensais pas vraiment !". Pourquoi voudriez-vous s'attendre à que cela soit possible ?
3 votes
Merci Dan pour l'édition. Je n'étais pas vraiment impoli, mais ta formulation est meilleure.
0 votes
@lwburk c'est possible en c#, mais les raisons pour lesquelles cela ne s'applique pas en js parce que l'infrastructure n'est pas là en js pour le faire. Je confirmais en gros que ce n'était pas possible.
0 votes
@Inerdial ce n'est pas ennuyeux, c'est plutôt agréable, mais les contraintes de temps pour cet aspect du remaniement ne le permettent pas pour le moment ; cela se fera plus tard dans le cadre d'un remaniement plus important.
0 votes
Duplicata possible : stackoverflow.com/questions/10651755/
2 votes
@RobertC.Barth C'est maintenant possible avec JavaScript aussi. Les fonctions async await n'ont pas encore été ratifiées dans la norme, mais il est prévu qu'elles le soient dans ES2017. Voir ma réponse ci-dessous pour plus de détails.
0 votes
@Wayne
async
est une syntaxe / construction. Il ne s'agit pas nécessairement d'un intention . Cette question fait explicitement la distinction entre les deux.0 votes
@javadba Je ne suis pas sûr de ce que vous essayez de dire, c'est pourquoi je suis assez confiant que la question n'est pas en fait explicitement faire la distinction à laquelle vous faites allusion
0 votes
@Wayne Sur le jvm, on peut lancer un thread séparé qui exécute les tâches de manière asynchrone par rapport au thread principal. Ensuite, sur le thread principal, on a le choix : attendre que ces tâches "asynchrones" se terminent ou continuer son activité. C'est la distinction entre la syntaxe et l'intention. Javascript n'a aucun moyen de convertir la syntaxe
async
syntaxe en un programme non asynchrone comportement . J'ai pu comprendre cette question assez bien à cet égard.0 votes
Deux réponses ci-dessous montrent comment c'est possible aujourd'hui dans le navigateur avec les Service Workers y dans Node.js avec node-fibers . Mais les deux ne sont pas recommandés et doivent être ÉVITÉS.