Les fonctions asynchrones , une fonctionnalité d'ES2017 , permettent de synchroniser le code asynchrone en utilisant des promesses (une forme particulière de code asynchrone) et le await
mot clé. Notez également dans les exemples de code ci-dessous le mot-clé async
devant le function
mot-clé qui signifie une fonction asynchrone / attente. Le await
mot-clé ne fonctionnera pas sans être dans une fonction pré-fixée avec le async
mot - clé. Comme il n'y a actuellement aucune exception à cela, cela signifie qu'aucune attente de niveau supérieur ne fonctionnera (le niveau supérieur attend, ce qui signifie une attente en dehors de toute fonction). Bien qu'il existe une proposition de haut niveauawait
.
ES2017 a été ratifié (c'est-à-dire finalisé) en tant que norme pour JavaScript le 27 juin 2017. L'attente asynchrone peut déjà fonctionner dans votre navigateur, mais sinon, vous pouvez toujours utiliser la fonctionnalité à l'aide d'un transpilateur javascript comme babel ou traceur . Chrome 55 prend entièrement en charge les fonctions asynchrones. Donc, si vous avez un navigateur plus récent, vous pourrez peut-être essayer le code ci-dessous.
Voir le tableau de compatibilité es2017 de kangax pour la compatibilité du navigateur.
Voici un exemple de fonction d'attente asynchrone appelée doAsync
qui prend trois pauses d'une seconde et imprime le décalage horaire après chaque pause à partir de l'heure de début:
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é wait est placé avant une valeur promise (dans ce cas, la valeur promise est la valeur retournée par la fonction doSomethingAsync), le mot-clé wait suspendra l'exécution de l'appel de fonction, mais ne suspendra aucune autre fonction et continuera exécuter un autre code jusqu'à ce que la promesse soit résolue. Une fois la promesse résolue, elle déballera la valeur de la promesse et vous pouvez penser que l'expression attendre et promettre est désormais remplacée par cette valeur non emballée.
Ainsi, puisque wait s'arrête juste attend puis décompresse une valeur avant d'exécuter le reste de la ligne, vous pouvez l'utiliser dans les boucles et les appels de fonction internes 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 afin que vous puissiez l'utiliser comme une promesse avec un chaînage comme je le fais ci-dessus ou dans une autre fonction d'attente asynchrone.
La fonction ci-dessus attendrait chaque réponse avant d'envoyer une autre demande si vous souhaitez envoyer les demandes 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'encapsuler dans un catch catch ou ignorer le try catch et laisser l'erreur se propager à l'appel des fonctions async / wait. Vous devez faire attention à ne pas laisser les erreurs de promesse non gérées, en particulier dans Node.js. Voici quelques exemples qui montrent comment fonctionnent les 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 ici, vous pouvez voir les propositions finies pour les prochaines versions d'ECMAScript.
Une alternative à cela qui peut être utilisée avec seulement ES2015 (ES6) est d'utiliser une fonction spéciale qui encapsule une fonction de générateur. Les fonctions génératrices ont un mot-clé yield qui peut être utilisé pour répliquer le mot-clé wait 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 de choses que ce que fait la fonction d'attente asynchrone. Si vous voulez une enveloppe de fonction de générateur qui peut être utilisé pour async répliquées attendre que je vérifierais co.js . Par ailleurs, la fonction de co, tout comme les fonctions d'attente asynchrone, renvoie une promesse. Honnêtement, à ce stade, la compatibilité du navigateur est à peu près la même pour les fonctions de générateur et les fonctions asynchrones, donc si vous voulez juste la fonctionnalité d'attente asynchrone, vous devez utiliser les fonctions Async sans co.js.
La prise en charge du navigateur est en fait assez bonne maintenant pour les fonctions Async (à partir de 2017) dans tous les principaux navigateurs actuels (Chrome, Safari et Edge) sauf IE.