理解Koa2中的async&await的用法


Posted in Javascript onFebruary 05, 2018

Koa是一款非常著名的Node服务端框架,有1.x版本和2.x版本。前者使用了generator来进行异步操作,后者则用了最新的async/await方案

一开始使用这种写法的时候,我遇到一个问题,代码如下:

const Koa = require('koa');
const app = new Koa();

const doSomething = time => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('task done!')
    }, time)
  })
}

// 用来打印请求信息
app.use((ctx, next) => {
  console.log(`${ctx.method}:::${ctx.url}`)
  next()
})

app.use(async ctx => {
  const result = await doSomething(3000)
  console.log(result);
  ctx.body = result
})

app.listen(3000);

让我们测试一下:curl http://localhost:3000

期望结果:

(3秒后...)task done!

然而现实却是:

(立即)
Not Found

什么鬼?为什么没有按照预期执行?这就需要我们来理解下Koa中中间件是如何串联起来的了。翻一下源码,将middlewares串联起来的代码如下:

function compose (middleware) {
 return function (context, next) {
  // 这个index用来计数,防止next被多次调用
  let index = -1
  // 执行入口
  return dispatch(0)
  
  function dispatch (i) {
   // 如果next被多次调用,报异常
   if (i <= index) return Promise.reject(new Error('next() called multiple times'))
   index = i
   // 取出第一个middleware
   let fn = middleware[i]
   // 将最初传入的next作为最后一个函数执行
   if (i === middleware.length) fn = next
   if (!fn) return Promise.resolve()
   try {
    /**
    这里就是关键了,Promise.resolve是什么意思呢?
     Promise.resolve方法有下面三种形式:
     
     Promise.resolve(value);
     Promise.resolve(promise);
     Promise.resolve(theanable);
     
    这三种形式都会产生一个新的Promise。其中:

    第一种形式提供了自定义Promise的值的能力,它与Promise.reject(reason)对应。两者的不同,在于得到的Promise的状态不同。

    第二种形式,提供了创建一个Promise的副本的能力。

    第三种形式,是将一个类似Promise的对象转换成一个真正的Promise对象。它的一个重要作用是将一个其他实现的Promise对象封装成一个当前实现的Promise对象。例如你正在用bluebird,但是现在有一个Q的Promise,那么你可以通过此方法把Q的Promise变成一个bluebird的Promise。第二种形式可以归在第三种里面
    
    **/
    return Promise.resolve(fn(context, function next () {
     // 执行下一个middleware,返回结果也是一个Promise
     return dispatch(i + 1)
    }))
   } catch (err) {
    return Promise.reject(err)
   }
  }
 }
}

有了以上基础,我们再来看一下之前的问题,为什么response没有等到第二个middleware执行完成就立即返回了呢?

因为第一个middleware并不是一个异步函数啊。

由于每次next方法的执行,实际上都是返回了一个Promise对象,所以如果我们在某个middleware中执行了异步操作,要想等待其完成,就要在执行这个middleware之前添加await

那我们来改写一下之前的代码

app.use(async (ctx, next) => {
  console.log(`${ctx.method}:::${ctx.url}`)
  await next()
})

app.use(async ctx => {
  const result = await doSomething(3000)
  console.log(result);
  ctx.body = result
})

好了,没有问题,一切如期望执行:clap:

错误处理

借助了Promise强大的功力,配合async/await语法,我们只需要把try/catch的操作写在最外层的middleware中,就可以捕获到之后所有中间件的异常!

app.use(async (ctx, next) => {
  try{
    await next()
  }catch(err){
    console.log(err)
  }
})

app.use(async (ctx)=>{
  throw new Error('something wrong!')
  ctx.body = 'Hello'
})

基于中间件链的完全控制,并且基于 Promise 的事实使得一切都变得容易操作起来。不再是到处的 if (err) return next(err) 而只有 promise

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
firefox 和 ie 事件处理的细节,研究,再研究 书写同时兼容ie和ff的事件处理代码
Apr 12 Javascript
javascript Split方法,indexOf方法、lastIndexOf 方法和substring 方法
Mar 21 Javascript
jQuery函数的等价原生函数代码示例
May 27 Javascript
用javascript添加控件自定义属性解析
Nov 25 Javascript
javascript中对Attr(dom中属性)的操作示例讲解
Dec 02 Javascript
js实现文本框只允许输入数字并限制数字大小的方法
Aug 19 Javascript
jQuery zTree加载树形菜单功能
Feb 25 Javascript
总结JavaScript三种数据存储方式之间的区别
May 03 Javascript
JS库 Highlightjs 添加代码行号的实现代码
Sep 13 Javascript
使用express+multer实现node中的图片上传功能
Feb 02 Javascript
改变vue请求过来的数据中的某一项值的方法(详解)
Mar 08 Javascript
多页vue应用的单页面打包方法(内含打包模式的应用)
Jun 11 Javascript
zTree 树插件实现全国五级地区点击后加载的示例
Feb 05 #Javascript
使用vue如何构建一个自动建站项目
Feb 05 #Javascript
在 webpack 中使用 ECharts的实例详解
Feb 05 #Javascript
在Vue中使用echarts的方法
Feb 05 #Javascript
JavaScript中Object基础内部方法图
Feb 05 #Javascript
基于axios封装fetch方法及调用实例
Feb 05 #Javascript
JS设计模式之观察者模式实现实时改变页面中金额数的方法
Feb 05 #Javascript
You might like
php xfocus防注入资料
2008/04/27 PHP
PHP生成图片验证码、点击切换实例
2014/06/25 PHP
实例介绍PHP的Reflection反射机制
2014/08/05 PHP
yii用户注册表单验证实例
2015/12/26 PHP
PHP类和对象相关系统函数与运算符小结
2016/09/28 PHP
PHP实现生成推广海报的方法详解
2018/03/14 PHP
js判断样式className同时增加class或删除class
2013/01/30 Javascript
javascript数组的使用
2013/03/28 Javascript
删除条目时弹出的确认对话框
2014/06/05 Javascript
jquery获取选中的文本和值的方法
2014/07/08 Javascript
优化Node.js Web应用运行速度的10个技巧
2014/09/03 Javascript
JQuery中模拟image的ajaxPrefilter与ajaxTransport处理
2015/06/19 Javascript
laypage分页控件使用实例详解
2016/05/19 Javascript
jQuery实现带遮罩层效果的blockUI弹出层示例【附demo源码下载】
2016/09/14 Javascript
js调用父框架函数与弹窗调用父页面函数的简单方法
2016/11/01 Javascript
15个非常实用的JavaScript代码片段
2016/12/18 Javascript
vue-resource 拦截器(interceptor)的使用详解
2017/07/04 Javascript
微信小程序数据存储与取值详解
2018/01/30 Javascript
原生JS实现旋转轮播图+文字内容切换效果【附源码】
2018/09/29 Javascript
JavaScript变量作用域及内存问题实例分析
2019/06/10 Javascript
vue使用better-scroll实现滑动以及左右联动
2020/06/30 Javascript
[37:45]2014 DOTA2国际邀请赛中国区预选赛5.21 DT VS Orenda
2014/05/22 DOTA
[02:41]《西雅图我们来了》2015国际邀请赛出征全记录
2015/07/23 DOTA
解决Python 遍历字典时删除元素报异常的问题
2016/09/11 Python
python 实现自动远程登陆scp文件实例代码
2017/03/13 Python
Python代码缩进和测试模块示例详解
2018/05/07 Python
关于sys.stdout和print的区别详解
2019/12/05 Python
在tensorflow中设置使用某一块GPU、多GPU、CPU的操作
2020/02/07 Python
总经理秘书工作职责
2013/12/26 职场文书
转预备党员政审材料
2014/02/06 职场文书
大学新生军训自我鉴定
2014/03/18 职场文书
绿色学校实施方案
2014/03/31 职场文书
党的群众路线教育实践活动个人整改措施范文
2014/11/04 职场文书
繁星春水读书笔记
2015/06/30 职场文书
深度学习小工程练习之垃圾分类详解
2021/04/14 Python
Nginx工作模式及代理配置的使用细节
2022/03/21 Servers