Cet article présente principalement les connaissances pertinentes sur les fonctions Async et Await dans Node.js. Il est très bon et a une valeur de référence. Les amis dans le besoin peuvent s'y référer
Dans cet article, vous apprendrez comment. pour utiliser la fonction asynchrone (async/await) dans Node.js pour simplifier le rappel ou la promesse.
Des structures de langage asynchrones existent déjà dans d'autres langages, comme async/await de c#, les coroutines de Kotlin et les goroutines de go, avec Depuis la sortie de Node.js 8, la fonction asynchrone tant attendue est également implémentée par défaut.
Qu'est-ce que la fonction asynchrone dans Node ?
Lorsqu'une fonction est déclarée comme fonction Async, elle renvoie un objet AsyncFunction. Elles sont similaires aux générateurs dans la mesure où l'exécution peut être suspendue. La seule différence est qu'ils renvoient une promesse au lieu d'un objet {value: any, done: Boolean}. Cependant, ils sont toujours très similaires et vous pouvez utiliser le package co pour obtenir les mêmes fonctionnalités.
Dans une fonction asynchrone, vous pouvez attendre que la promesse se termine ou capturer la raison de son rejet.
Si vous souhaitez implémenter une partie de votre propre logique dans Promise
function handler (req, res) { return request('https://user-handler-service') .catch((err) => { logger.error('Http error', err) error.logged = true throw err }) .then((response) => Mongo.findOne({ user: response.body.user })) .catch((err) => { !error.logged && logger.error('Mongo error', err) error.logged = true throw err }) .then((document) => executeLogic(req, res, document)) .catch((err) => { !error.logged && console.error(err) res.status(500).send() }) }
Vous pouvez utiliser async/await pour que ce code ressemble à du code exécuté de manière synchrone
async function handler (req, res) { let response try { response = await request('https://user-handler-service') } catch (err) { logger.error('Http error', err) return res.status(500).send() } let document try { document = await Mongo.findOne({ user: response.body.user }) } catch (err) { logger.error('Mongo error', err) return res.status(500).send() } executeLogic(document, req, res) }
Dans l'ancienne version v8, s'il y a un rejet de promesse qui n'est pas géré, vous recevrez un avertissement et vous n'aurez pas besoin de créer une fonction d'écoute des erreurs de rejet. Il est toutefois recommandé de quitter votre application dans ce cas. Car lorsque vous ne gérez pas les erreurs, l’application est dans un état inconnu.
process.on('unhandledRejection', (err) => { console.error(err) process.exit(1) })
modèle de fonction asynchrone
Lorsqu'il s'agit d'opérations asynchrones, il existe de nombreux exemples de les faire ressembler à du code synchrone. Si vous utilisez Promise ou des rappels pour résoudre le problème, vous devez utiliser un modèle très complexe ou une bibliothèque externe.
C'est une situation très compliquée lorsque vous devez utiliser l'acquisition asynchrone de données en boucle ou utiliser des conditions if-else.
Mécanisme de restauration exponentielle
Utiliser Promise pour implémenter une logique de restauration est assez maladroit
function requestWithRetry (url, retryCount) { if (retryCount) { return new Promise((resolve, reject) => { const timeout = Math.pow(2, retryCount) setTimeout(() => { console.log('Waiting', timeout, 'ms') _requestWithRetry(url, retryCount) .then(resolve) .catch(reject) }, timeout) }) } else { return _requestWithRetry(url, 0) } } function _requestWithRetry (url, retryCount) { return request(url, retryCount) .catch((err) => { if (err.statusCode && err.statusCode >= 500) { console.log('Retrying', err.message, retryCount) return requestWithRetry(url, ++retryCount) } throw err }) } requestWithRetry('http://localhost:3000') .then((res) => { console.log(res) }) .catch(err => { console.error(err) })
Le code est impressionnant à watch C'est un casse-tête et vous ne voulez pas voir du code comme celui-ci. Nous pouvons refaire cet exemple en utilisant async/await pour le rendre plus simple
function wait (timeout) { return new Promise((resolve) => { setTimeout(() => { resolve() }, timeout) }) } async function requestWithRetry (url) { const MAX_RETRIES = 10 for (let i = 0; i <= MAX_RETRIES; i++) { try { return await request(url) } catch (err) { const timeout = Math.pow(2, i) console.log('Waiting', timeout, 'ms') await wait(timeout) console.log('Retrying', err.message, i) } } }
Le code ci-dessus semble très confortable, n'est-ce pas
Valeur intermédiaire
Pas aussi effrayant que l'exemple précédent, si vous avez 3 fonctions asynchrones qui dépendent les unes des autres à tour de rôle, alors vous devez choisir parmi plusieurs solutions laides.
functionA renvoie une promesse, puis functionB a besoin de cette valeur et functioinC a besoin des valeurs une fois la fonctionA et la fonctionB terminées.
Option 1 : puis sapin de Noël
function executeAsyncTask () { return functionA() .then((valueA) => { return functionB(valueA) .then((valueB) => { return functionC(valueA, valueB) }) }) }
En utilisant cette solution, nous pouvons alors obtenir valeurA et valeurB dans le troisième, et ensuite nous pouvons passer au premier two obtient alors également les valeurs de valueA et valueB. Vous ne pouvez pas aplatir le sapin de Noël (ruiner l'enfer) ici, si vous le faites, vous perdrez la fermeture et valueA ne sera pas disponible dans functioinC.
Option 2 : Passer à la portée de niveau supérieur
function executeAsyncTask () { let valueA return functionA() .then((v) => { valueA = v return functionB(valueA) }) .then((valueB) => { return functionC(valueA, valueB) }) }
Dans cet arbre de Noël, nous utilisons une valeur de retenue de portée plus élevée, car valeurA La portée est en dehors de tout alors scopes, afin que functionC puisse obtenir la valeur de la première fonctionA à terminer.
Il s'agit d'une syntaxe très "correcte" pour aplatir la chaîne .then, cependant, de cette façon, nous devons utiliser deux variables valueA et v pour conserver la même valeur.
Option 3 : Utilisez un tableau supplémentaire
function executeAsyncTask () { return functionA() .then(valueA => { return Promise.all([valueA, functionB(valueA)]) }) .then(([valueA, valueB]) => { return functionC(valueA, valueB) }) }
Utilisez un tableau dans le then de la fonction functionA pour renvoyer valueA et Promise ensemble, ce qui peut efficacement aplatir Transformer le Sapin de Noël (rappel en enfer).
Option 4 : Écrire une fonction d'assistance
const converge = (...promises) => (...args) => { let [head, ...tail] = promises if (tail.length) { return head(...args) .then((value) => converge(...tail)(...args.concat([value]))) } else { return head(...args) } } functionA(2) .then((valueA) => converge(functionB, functionC)(valueA))
C'est faisable, écrivez une fonction d'assistance pour protéger la déclaration de variable contextuelle. Mais un tel code est très difficile à lire, surtout pour les personnes qui ne sont pas familiarisées avec ces magies.
Utiliser async/await nos problèmes ont disparu comme par magie
async function executeAsyncTask () { const valueA = await functionA() const valueB = await functionB(valueA) return function3(valueA, valueB) }
Utiliser async/await pour gérer plusieurs requêtes parallèles
C'est presque la même chose que celle ci-dessus, si vous voulez pour le faire en même temps L'exécution de plusieurs tâches asynchrones puis l'utilisation de leurs valeurs à différents endroits peuvent être facilement réalisées en utilisant async/await.
async function executeParallelAsyncTasks () { const [ valueA, valueB, valueC ] = await Promise.all([ functionA(), functionB(), functionC() ]) doSomethingWith(valueA) doSomethingElseWith(valueB) doAnotherThingWith(valueC) }
Méthode d'itération de tableau
Vous pouvez utiliser des fonctions asynchrones dans les méthodes de cartographie, de filtrage et de réduction, bien qu'elles n'aient pas l'air très Intuitif, mais vous pouvez expérimenter le code suivant dans la console.
1.map
function asyncThing (value) { return new Promise((resolve, reject) => { setTimeout(() => resolve(value), 100) }) } async function main () { return [1,2,3,4].map(async (value) => { const v = await asyncThing(value) return v * 2 }) } main() .then(v => console.log(v)) .catch(err => console.error(err))
2.filter
function asyncThing (value) { return new Promise((resolve, reject) => { setTimeout(() => resolve(value), 100) }) } async function main () { return [1,2,3,4].filter(async (value) => { const v = await asyncThing(value) return v % 2 === 0 }) } main() .then(v => console.log(v)) .catch(err => console.error(err))
3.reduce
function asyncThing (value) { return new Promise((resolve, reject) => { setTimeout(() => resolve(value), 100) }) } async function main () { return [1,2,3,4].reduce(async (acc, value) => { return await acc + await asyncThing(value) }, Promise.resolve(0)) } main() .then(v => console.log(v)) .catch(err => console.error(err))
Solution :
[ Promise { <pending> }, Promise { <pending> }, Promise { <pending> }, Promise { <pending> } ] [ 1, 2, 3, 4 ] 10
S'il s'agit de données d'itération de carte, vous verrez que la valeur de retour est [2, 4, 6, 8]. Le seul problème est que chaque valeur est enveloppée dans un Fonction AsyncFunction
dans une promesse, donc si vous souhaitez obtenir leurs valeurs, vous devez transmettre le tableau à Promise.All() pour déballer la promesse.
main() .then(v => Promise.all(v)) .then(v => console.log(v)) .catch(err => console.error(err)) 一开始你会等待 Promise 解决,然后使用map遍历每个值 function main () { return Promise.all([1,2,3,4].map((value) => asyncThing(value))) } main() .then(values => values.map((value) => value * 2)) .then(v => console.log(v)) .catch(err => console.error(err))
Cela semble plus facile ?
La version async/await est toujours utile si vous avez une logique synchrone de longue durée et une autre tâche asynchrone de longue durée dans votre itérateur
这种方式当你能拿到第一个值,就可以开始做一些计算,而不必等到所有 Promise 完成才运行你的计算。尽管结果包裹在 Promise 中,但是如果按顺序执行结果会更快。
关于 filter 的问题
你可能发觉了,即使上面filter函数里面返回了 [ false, true, false, true ] , await asyncThing(value) 会返回一个 promise 那么你肯定会得到一个原始的值。你可以在return之前等待所有异步完成,在进行过滤。
Reducing很简单,有一点需要注意的就是需要将初始值包裹在 Promise.resolve 中
重写基于callback的node应用成
Async 函数默认返回一个 Promise ,所以你可以使用 Promises 来重写任何基于 callback 的函数,然后 await 等待他们执行完毕。在node中也可以使用 util.promisify 函数将基于回调的函数转换为基于 Promise 的函数
重写基于Promise的应用程序
要转换很简单, .then 将Promise执行流串了起来。现在你可以直接使用`async/await。
function asyncTask () { return functionA() .then((valueA) => functionB(valueA)) .then((valueB) => functionC(valueB)) .then((valueC) => functionD(valueC)) .catch((err) => logger.error(err)) }
转换后
async function asyncTask () { try { const valueA = await functionA() const valueB = await functionB(valueA) const valueC = await functionC(valueB) return await functionD(valueC) } catch (err) { logger.error(err) } } Rewriting Nod
使用 Async/Await 将很大程度上的使应用程序具有高可读性,降低应用程序的处理复杂度(如:错误捕获),如果你也使用 node v8+的版本不妨尝试一下,或许会有新的收获。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
在angularJs-$http中如何实现百度搜索时的动态下拉框
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!