理解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插件之信息弹出框showInfoDialog(成功/错误/警告/通知/背景遮罩)
Jan 09 Javascript
用Javascript获取页面元素的具体位置
Dec 09 Javascript
jQuery中hasClass()方法用法实例
Jan 06 Javascript
js控制网页前进和后退的方法
Jun 08 Javascript
jQuery根据用户电脑是mac还是pc加载对应样式的方法
Jun 26 Javascript
解决JS无法调用Controller问题的方法
Dec 31 Javascript
JavaScript笔记之数据属性和存储器属性
Mar 31 Javascript
微信小程序 label 组件详解及简单实例
Jan 10 Javascript
vue 过滤器filter实例详解
Mar 14 Javascript
不使用JavaScript实现菜单的打开和关闭效果demo
May 01 Javascript
使用Layui搭建后台管理界面的操作方法
Sep 20 Javascript
JavaScript设计模式--简单工厂模式实例分析【XHR工厂案例】
May 23 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.ini中添加extension=php_mysqli.dll指令的说明
2007/06/14 PHP
在Ubuntu 14.04上部署 PHP 环境及 WordPress
2014/09/02 PHP
php准确计算复活节日期的方法
2015/04/18 PHP
php中序列化与反序列化详解
2017/02/13 PHP
PHP7导出Excel报ERR_EMPTY_RESPONSE解决方法
2019/04/16 PHP
JQuery实现绚丽的横向下拉菜单
2013/12/19 Javascript
js+jquery常用知识点汇总
2015/03/03 Javascript
jQuery设置指定网页元素宽度和高度的方法
2015/03/25 Javascript
javascript中sort() 方法使用详解
2015/08/30 Javascript
两款JS脚本判断手机浏览器类型跳转WAP手机网站
2015/10/16 Javascript
只要1K 纯JS脚本送你一朵3D红色玫瑰
2016/08/09 Javascript
javascript实现图片左右滚动效果【可自动滚动,有左右按钮】
2016/09/19 Javascript
JS匿名函数类生成方式实例分析
2016/11/26 Javascript
Webpack执行命令参数详解
2017/06/17 Javascript
Node实战之不同环境下配置文件使用教程
2018/01/02 Javascript
浅谈Vue Element中Select下拉框选取值的问题
2018/03/01 Javascript
Element-ui自定义table表头、修改列标题样式、添加tooltip、:render-header使用
2019/04/11 Javascript
在vue中使用jsx语法的使用方法
2019/09/30 Javascript
在vue中实现禁止屏幕滚动,禁止屏幕滑动
2020/07/22 Javascript
Vue页面跳转传递参数及接收方式
2020/09/09 Javascript
vue3.0搭配.net core实现文件上传组件
2020/10/29 Javascript
[02:31]2014DOTA2国际邀请赛2009专访:干爹表现出乎意料 看好DK杀回决赛
2014/07/20 DOTA
[42:23]完美世界DOTA2联赛PWL S3 Forest vs Rebirth 第二场 12.10
2020/12/13 DOTA
如何将python中的List转化成dictionary
2016/08/15 Python
Django中ORM表的创建和增删改查方法示例
2017/11/15 Python
numpy找出array中的最大值,最小值实例
2018/04/03 Python
python保留小数位的三种实现方法
2020/01/07 Python
CSS3 实现弹幕的示例代码
2017/08/07 HTML / CSS
Staples加拿大官方网站:办公用品一站式采购
2016/09/25 全球购物
港湾网络笔试题
2014/04/19 面试题
党员公开承诺书
2014/03/25 职场文书
三八妇女节致辞
2015/07/31 职场文书
个人工作决心书
2015/09/22 职场文书
golang goroutine顺序输出方式
2021/04/29 Golang
Python如何解决secure_filename对中文不支持问题
2021/07/16 Python
利用Python多线程实现图片下载器
2022/03/25 Python