Maison > interface Web > js tutoriel > Utilisation des fonctions Async et Await dans Node.js

Utilisation des fonctions Async et Await dans Node.js

亚连
Libérer: 2018-06-05 14:49:35
original
2663 Les gens l'ont consulté

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()
 })
}
Copier après la connexion

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)
}
Copier après la connexion

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)
})
Copier après la connexion

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)
 })
Copier après la connexion

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(&#39;Waiting&#39;, timeout, &#39;ms&#39;)
  await wait(timeout)
  console.log(&#39;Retrying&#39;, err.message, i)
 }
 }
}
Copier après la connexion

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)
  })
 })
}
Copier après la connexion

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)
 })
}
Copier après la connexion

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)
 })
}
Copier après la connexion

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))
Copier après la connexion

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)
}
Copier après la connexion

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)
}
Copier après la connexion

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))
Copier après la connexion

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))
Copier après la connexion

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))
Copier après la connexion

Solution :

[ Promise { <pending> }, Promise { <pending> }, Promise { <pending> }, Promise { <pending> } ]
[ 1, 2, 3, 4 ]
10
Copier après la connexion

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))
Copier après la connexion

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))
}
Copier après la connexion

转换后

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
Copier après la connexion

使用 Async/Await 将很大程度上的使应用程序具有高可读性,降低应用程序的处理复杂度(如:错误捕获),如果你也使用 node v8+的版本不妨尝试一下,或许会有新的收获。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

如何获取Vue中的this.$router.push参数

在angularJs-$http中如何实现百度搜索时的动态下拉框

在angularjs数组中如何判断是否含有某个元素

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!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal