Node.js中的Async和Await函数解析

本文主要和大家介绍了node.js中的async和await函数的相关知识,你将学习如何使用node.js中的async函数(async/await)来简化callback或promise.非常不错,具有参考借鉴价值,需要的朋友可以参考下,希望能帮助到大家。

异步语言结构在其他语言中已经存在了,像c#的async/await、Kotlin的coroutines、go的goroutines,随着Node.js 8的发布,期待已久的async函数也在其中默认实现了。

Node中的async函数是什么?

当函数声明为一个Async函数它会返回一个 AsyncFunction 对象,它们类似于 Generator 因为执可以被暂停。唯一的区别是它们返回的是 Promise 而不是 { value: any, done: Boolean } 对象。不过它们还是非常相似,你可以使用 co 包来获取同样的功能。

在async函数中,可以等待 Promise 完成或捕获它拒绝的原因。

如果你要在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() })}

登录后复制

可以使用 async/await 让这个代码看起来像同步执行的代码

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)}

登录后复制

在老的v8版本中,如果有有个 promise 的拒绝没有被处理你会得到一个警告,可以不用创建一个拒绝错误监听函数。然而,建议在这种情况下退出你的应用程序。因为当你不处理错误时,应用程序处于一个未知的状态。

process.on('unhandledRejection', (err) => {  console.error(err) process.exit(1)})

登录后复制

async函数模式

在处理异步操作时,有很多例子让他们就像处理同步代码一样。如果使用 Promise 或 callbacks 来解决问题时需要使用很复杂的模式或者外部库。

当需要再循环中使用异步获取数据或使用 if-else 条件时就是一种很复杂的情况。

指数回退机制

使用 Promise 实现回退逻辑相当笨拙

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) })

登录后复制

代码看的让人很头疼,你也不会想看这样的代码。我们可以使用async/await重新这个例子,使其更简单

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) } }}

登录后复制

上面代码看起来很舒服对不对

中间值

不像前面的例子那么吓人,如果你有3个异步函数依次相互依赖的情况,那么你必须从几个难看的解决方案中进行选择。

functionA 返回一个 Promise ,那么 functionB 需要这个值而 functioinC 需要 functionA 和 functionB 完成后的值。

方案1: then 圣诞树

function executeAsyncTask () { return functionA() .then((valueA) => {  return functionB(valueA)  .then((valueB) => {      return functionC(valueA, valueB)  }) })}

登录后复制

用这个解决方案,我们在第三个 then 中可以获得 valueA 和 valueB ,然后可以向前面两个 then 一样获得 valueA 和 valueB 的值。这里不能将圣诞树(毁掉地狱)拉平,如果这样做的话会丢失闭包, valueA 在 functioinC 中将不可用。

方案2:移动到上一级作用域

function executeAsyncTask () { let valueA return functionA() .then((v) => {  valueA = v  return functionB(valueA) }) .then((valueB) => {  return functionC(valueA, valueB) })}

登录后复制

在这颗圣诞树中,我们使用更高的作用域保变量 valueA ,因为 valueA 作用域在所有的 then 作用域外面,所以 functionC 可以拿到第一个 functionA 完成的值。

这是一个很有效扁平化 .then 链”正确”的语法,然而,这种方法我们需要使用两个变量 valueA 和 v 来保存相同的值。

方案3:使用一个多余的数组

function executeAsyncTask () { return functionA() .then(valueA => {  return Promise.all([valueA, functionB(valueA)]) }) .then(([valueA, valueB]) => {  return functionC(valueA, valueB) })}

登录后复制

在函数 functionA 的 then 中使用一个数组将 valueA 和 Promise 一起返回,这样能有效的扁平化圣诞树(回调地狱)。

方案4:写一个帮助函数

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))

登录后复制

这样是可行的,写一个帮助函数来屏蔽上下文变量声明。但是这样的代码非常不利于阅读,对于不熟悉这些魔法的人就更难了。

使用 async/await 我们的问题神奇般的消失

async function executeAsyncTask () { const valueA = await functionA() const valueB = await functionB(valueA) return function3(valueA, valueB)}

登录后复制

使用 async/await 处理多个平行请求

和上面一个差不多,如果你想一次执行多个异步任务,然后在不同的地方使用它们的值可以使用 async/await 轻松搞定。

async function executeParallelAsyncTasks () { const [ valueA, valueB, valueC ] = await Promise.all([ functionA(), functionB(), functionC() ]) doSomethingWith(valueA) doSomethingElseWith(valueB) doAnotherThingWith(valueC)}

登录后复制

数组迭代方法

你可以在 map 、 filter 、 reduce 方法中使用async函数,虽然它们看起来不是很直观,但是你可以在控制台中实验以下代码。

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))

登录后复制

解决方案:

[ Promise {  }, Promise {  }, Promise {  }, Promise {  } ][ 1, 2, 3, 4 ]10

登录后复制

如果是map迭代数据你会看到返回值为 [ 2, 4, 6, 8 ] ,唯一的问题是每个值被 AsyncFunction 函数包裹在了一个 Promise 中

所以如果想要获得它们的值,需要将数组传递给 Promise.All() 来解开 Promise 的包裹。

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))

登录后复制

这样好像更简单一些?

如果在你的迭代器中如果你有一个长时间运行的同步逻辑和另一个长时间运行的异步任务,async/await版本任然常有用

这种方式当你能拿到第一个值,就可以开始做一些计算,而不必等到所有 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+的版本不妨尝试一下,或许会有新的收获。

相关推荐:

ES6之async+await 同步/异步方案

NodeJs通过async和await处理异步的方法

ES7的async/await用法实例详解

以上就是Node.js中的Async和Await函数解析的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2786032.html

(0)
上一篇 2025年3月8日 17:38:59
下一篇 2025年3月1日 16:52:17

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 线浏览PDF文件的几个实用js插件

    在web开发当中我们经常需要进行需要在线浏览PDf内容,在线嵌入动态pdf,传统的解决方法安装客户端PDF阅读器,如果是在谷歌是可以在线浏览的,因为他自身就带了一些插件,以前遇到这样的问题往往是费时又费力,本文主要给大家分享一系列使用jav…

    2025年3月8日 编程技术
    200
  • js弹窗基础讲解

    我们在一些实例中用到了alert()方法、prompt()方法、prompt()方法,他们都是在屏幕上弹出一个对话框,并且在上面显示括号内的内容,使用这种方法使得页面的交互性更精彩,实际上我们经常会在进行网页浏览时简单这种类型的对话框,在用…

    2025年3月8日 编程技术
    200
  • JS函数实例详解

    函数是由事件驱动的或者当它被调用时执行的可重复代码块,JavaScript函数语法,函数就是包裹在花括号中的代码块,前面使用了关键词function: function functionname(){     这里是要执行的代码 } 当调用…

    编程技术 2025年3月8日
    200
  • js如何获取今天周几代码

    本文主要和大家分享js如何获取今天周几代码,希望能帮助到大家。   1.初级方法     var week = new Date().getDay(),str=’今天是星期’;    if(week === 0){str += ‘日’}..…

    编程技术 2025年3月8日
    200
  • js代码实现文档下载的方法

    在项目开发过程中,有时候没办法直接使用下载系统中的文件,可以采用以下方法去实现:  帮助  $(“#help”).on(“click”,function(){ var flag=window.confirm(“确定下载说明文档吗?”); /…

    编程技术 2025年3月8日
    200
  • js实现md5加密插件代码分享

    本文主要和大家分享js实现md5加密插件代码,希望能帮助到大家。 //使用方法://引入文件//var MD5=MD5.createMD5String(yourSrting);(function(globle,factory){//判断执行…

    编程技术 2025年3月8日
    200
  • 简单理解JS闭包

    JS中闭包是个很重要的概念,个人理解就是对函数调用者之间变量的访问控制的环境,本文将简单介绍一下什么是JS闭包。 function Person(){  var name=’stt’;  function sayName(){    con…

    编程技术 2025年3月8日
    200
  • js编译和执行顺序详解

    Javascript是一段一段执行的,以script标签来分割,执行每一段之前,都有一个“预编译”, 预编译干的活有两:1.声明所有var变量(初始为undefined),2.解析定义式函数语句。  有个关于 “window作用域下,a =…

    编程技术 2025年3月8日
    200
  • Eclipse去除js文件验证错误解决办法

    本文主要是对eclipse去除js(javascript)验证错误进行了介绍。在eclipse中,js文件常常会报错。可以通过如下几个步骤解决: 第一步:去除eclipse的JS验证:将windows->preference->…

    编程技术 2025年3月8日
    200
  • Vue.js源码学习provide 和 inject

    在 vue.js 的 2.2.0+ 版本中添加加了 provide 和 inject 选项。他们成对出现,用于父级组件向下传递数据。  源码位置 和之前一样,初始化的方法都是在 Vue 的 _init 方法中的。   // src/core…

    编程技术 2025年3月8日
    200

发表回复

登录后才能评论