理解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 相关文章推荐
jquery利用event.which方法获取键盘输入值的代码
Oct 09 Javascript
点弹代码 点击页面任何位置都可以弹出页面效果代码
Sep 17 Javascript
用JavaScript修改CSS属性的代码
May 06 Javascript
使用js操作cookie的一点小收获分享
Sep 03 Javascript
详解JavaScript中数组的相关知识
Jul 29 Javascript
jquery checkbox无法用attr()二次勾选问题的解决方法
Jul 22 Javascript
JS文件上传神器bootstrap fileinput详解
Jan 28 Javascript
weUI应用之JS常用信息提示弹层的封装
Nov 21 Javascript
web3.js增加eth.getRawTransactionByHash(txhash)方法步骤
Mar 15 Javascript
微信实现自动跳转到用其他浏览器打开指定APP下载
Feb 15 Javascript
Vue Autocomplete 自动完成功能简单示例
May 25 Javascript
scrapyd schedule.json setting 传入多个值问题
Aug 07 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 命名空间实例说明
2011/01/27 PHP
CI框架源码阅读,系统常量文件constants.php的配置
2013/02/28 PHP
thinkphp视图模型查询提示ERR: 1146:Table 'db.pr_order_view' doesn't exist的解决方法
2014/10/30 PHP
thinkphp模板赋值与替换实例简述
2014/11/24 PHP
PHP使用header()输出图片缓存实例
2014/12/09 PHP
php获取数组元素中头一个数组元素值的实现方法
2014/12/20 PHP
php自定义函数实现JS的escape的方法示例
2016/07/07 PHP
PHP登录验证码的实现与使用方法
2016/07/07 PHP
TP5框架实现的数据库备份功能示例
2020/04/05 PHP
爆炸式的JS圆形浮动菜单特效代码
2010/03/03 Javascript
jquery 注意事项与常用语法小结
2010/06/07 Javascript
基于JQuery的多标签实现代码
2012/09/19 Javascript
纯JS实现五子棋游戏兼容各浏览器(附源码)
2013/04/24 Javascript
javascript贪吃蛇完整版(源码)
2013/12/09 Javascript
PHP+jQuery实现随意拖动层并即时保存拖动位置
2015/04/30 Javascript
在Ubuntu系统上安装Node.JS的教程
2015/10/15 Javascript
利用JS实现页面删除并重新排序功能
2016/12/09 Javascript
ionic2中使用自动生成器的方法
2018/03/04 Javascript
Vue 中mixin 的用法详解
2018/04/23 Javascript
JS数组求和的常用方法实例小结
2019/01/07 Javascript
js中switch语句的学习笔记
2020/03/25 Javascript
python登录QQ邮箱发信的实现代码
2013/02/10 Python
用Python的线程来解决生产者消费问题的示例
2015/04/02 Python
Python安装使用命令行交互模块pexpect的基础教程
2016/05/12 Python
Win10下Python环境搭建与配置教程
2016/11/18 Python
浅谈pytorch和Numpy的区别以及相互转换方法
2018/07/26 Python
python数据结构学习之实现线性表的顺序
2018/09/28 Python
python利用pandas将excel文件转换为txt文件的方法
2018/10/23 Python
python3.8.1+selenium实现登录滑块验证功能
2020/05/22 Python
Pytorch生成随机数Tensor的方法汇总
2020/09/09 Python
python判断变量是否为列表的方法
2020/09/17 Python
HTML5新增的Css选择器、伪类介绍
2013/08/07 HTML / CSS
家长会邀请书
2014/01/25 职场文书
申请任职学生会干部自荐书范文
2014/02/13 职场文书
同学毕业留言寄语
2015/02/27 职场文书
IDEA 链接Mysql数据库并执行查询操作的完整代码
2021/05/20 MySQL