理解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 相关文章推荐
Extjs4 Treegrid 使用心得分享(经验篇)
Jul 01 Javascript
IE下JS读取xml文件示例代码
Aug 05 Javascript
JS中类或对象的定义说明
Mar 10 Javascript
Jquery弹出层插件ThickBox的使用方法
Dec 09 Javascript
JavaScript实现16进制颜色值转RGB的方法
Feb 09 Javascript
javascript实现框架高度随内容改变的方法
Jul 23 Javascript
swtich/if...else的替代语句
Aug 16 Javascript
Bootstrap每天必学之表格
Nov 23 Javascript
JS实现鼠标框选效果完整实例
Jun 20 Javascript
基于JS实现的随机数字抽签实例
Dec 08 Javascript
iview form清除校验状态的实现
Sep 19 Javascript
微信小程序定义和调用全局变量globalData的实现
Nov 01 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中webservice实现的简单架构方法及实例
2015/02/03 PHP
PHP框架Laravel学习心得体会
2015/10/28 PHP
php处理json格式数据经典案例总结
2016/05/19 PHP
[原创]PHPCMS遭遇会员投稿审核无效的解决方法
2017/01/11 PHP
PHP检查网站是否宕机的方法示例
2017/07/24 PHP
javascript 写类方式之十
2009/07/05 Javascript
js下用eval生成JSON对象
2010/09/17 Javascript
分享27个jQuery 表单插件集合推荐
2011/04/25 Javascript
javascript 获取iframe里页面中元素值的方法
2014/02/17 Javascript
js使用DOM操作实现简单留言板的方法
2015/04/10 Javascript
简单谈谈json跨域
2016/03/13 Javascript
JavaScript实现自定义媒体播放器方法介绍
2017/01/03 Javascript
node.js平台下利用cookie实现记住密码登陆(Express+Ejs+Mysql)
2017/04/26 Javascript
Vue学习笔记进阶篇之vue-cli安装及介绍
2017/07/18 Javascript
如何理解Vue的.sync修饰符的使用
2017/08/17 Javascript
angular.js4使用 RxJS 处理多个 Http 请求
2017/09/23 Javascript
深入浅析Vue.js中 computed和methods不同机制
2018/03/22 Javascript
JS实现的base64加密解密操作示例
2018/04/18 Javascript
详解小程序缓存插件(mrc)
2018/08/17 Javascript
详解nuxt路由鉴权(express模板)
2018/11/21 Javascript
JS数组方法concat()用法实例分析
2020/01/18 Javascript
小程序如何定位所在城市及发起周边搜索
2020/02/11 Javascript
vue组件入门知识全梳理
2020/09/21 Javascript
[10:34]DOTA2上海特级锦标赛全纪录
2016/03/25 DOTA
Python字符和字符值(ASCII或Unicode码值)转换方法
2015/05/21 Python
python解析基于xml格式的日志文件
2017/02/25 Python
pycharm运行程序时看不到任何结果显示的解决
2020/02/21 Python
Python爬虫之Selenium中frame/iframe表单嵌套页面
2020/12/04 Python
CSS3移动端vw+rem不依赖JS实现响应式布局的方法
2019/01/23 HTML / CSS
HTML5中meta属性的使用方法
2016/02/29 HTML / CSS
Holiday Inn中国官网:IHG旗下假日酒店预订
2018/04/08 全球购物
销售工作人员的自我评价分享
2013/11/10 职场文书
委托书怎样写
2014/08/30 职场文书
电子银行业务授权委托书
2014/10/10 职场文书
工程安全生产协议书
2014/11/21 职场文书
工作收入证明模板
2015/06/12 职场文书