理解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获取Select选择的Text和Value(详细汇总)
Jan 25 Javascript
jQuery Migrate 1.1.0 Released 注意事项
Jun 14 Javascript
详解AngularJS中的表格使用
Jun 16 Javascript
基于JavaScript实现仿京东图片轮播效果
Nov 06 Javascript
JavaScript利用HTML DOM进行文档操作的方法
Mar 28 Javascript
js与applet相互调用的方法
Jun 22 Javascript
jQuery模拟下拉框选择对应菜单的内容
Mar 07 Javascript
JS按钮闪烁功能的实现代码
Jul 21 Javascript
详解利用 Express 托管静态文件的方法
Sep 18 Javascript
详解ionic本地相册、拍照、裁剪、上传(单图完全版)
Oct 10 Javascript
通过vue-cli3构建一个SSR应用程序的方法
Sep 13 Javascript
何时/使用 Vue3 render 函数的教程详解
Jul 25 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
从康盛产品(discuz)提取出来的模板类
2011/06/28 PHP
php源代码安装常见错误与解决办法分享
2013/05/28 PHP
php弹出对话框实现重定向代码
2014/01/23 PHP
thinkphp循环结构用法实例
2014/11/24 PHP
Laravel Memcached缓存驱动的配置与应用方法分析
2016/10/08 PHP
js利用div背景,做一个竖线的效果。
2008/11/22 Javascript
javascript+css 网页每次加载不同样式的实现方法
2009/12/27 Javascript
jQuery中的bind绑定事件与文本框改变事件的临时解决方法
2010/08/13 Javascript
获取当前点击按钮的id用this.id实现
2014/03/17 Javascript
JS实现同一个网页布局滑动门和TAB选项卡实例
2015/09/23 Javascript
深入解析JavaScript编程中的this关键字使用
2015/11/09 Javascript
利用require.js与angular搭建spa应用的方法实例
2017/07/19 Javascript
vue中如何创建多个ueditor实例教程
2017/11/14 Javascript
Vue前端开发规范整理(推荐)
2018/04/23 Javascript
详解vue-cli 构建项目 vue-cli请求后台接口 vue-cli使用axios、sass、swiper
2018/05/28 Javascript
js实现二级联动简单实例
2020/01/11 Javascript
js实现贪吃蛇游戏 canvas绘制地图
2020/09/09 Javascript
python变量不能以数字打头详解
2016/07/06 Python
python列表的增删改查实例代码
2018/01/30 Python
解决Python一行输出不显示的问题
2018/12/03 Python
python 使用plt画图,去除图片四周的白边方法
2019/07/09 Python
python3实现往mysql中插入datetime类型的数据
2020/03/02 Python
Python Mock模块原理及使用方法详解
2020/07/07 Python
可自定义箭头样式的CSS3气泡提示框
2016/03/16 HTML / CSS
澳洲最大的时尚奢侈品电商平台:Cettire
2020/06/15 全球购物
Java语言的优势
2015/01/10 面试题
周年庆促销方案
2014/03/15 职场文书
群众路线领导干部个人对照检查材料(集锦)
2014/09/23 职场文书
领导班子作风建设年个人整改措施
2014/09/29 职场文书
旅游项目合作意向书
2015/05/08 职场文书
圆明园观后感
2015/06/03 职场文书
国富论读书笔记
2015/06/26 职场文书
婚庆主持词大全
2015/06/30 职场文书
教育教学读书笔记
2015/07/02 职场文书
CSS3实现360度循环旋转功能
2022/02/12 HTML / CSS
Tomcat starup.bat 脚本实现开机自启动
2022/04/20 Servers