理解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 相关文章推荐
document.execCommand()的用法小结
Jan 08 Javascript
JS 新增Cookie 取cookie值 删除cookie 举例详解
Oct 10 Javascript
js实现鼠标悬停图片上时滚动文字说明的方法
Feb 17 Javascript
javascript实现根据3原色制作颜色选择器的方法
Jul 17 Javascript
AngularJs 弹出模态框(model)
Apr 07 Javascript
基于javascript实现最简单的选项卡切换效果
May 16 Javascript
深入理解jQuery layui分页控件的使用
Aug 17 Javascript
Angularjs 实现一个幻灯片示例代码
Sep 08 Javascript
Layui tree 下拉菜单树的实例代码
Sep 21 Javascript
解决Layui数据表格显示无数据提示的问题
Nov 14 Javascript
vuex中store存储store.commit和store.dispatch的用法
Jul 24 Javascript
vue打包通过image-webpack-loader插件对图片压缩优化操作
Nov 12 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
yii2 页面底部加载css和js的技巧
2016/04/21 PHP
PHP通过微信跳转的Code参数获取用户的openid(关键代码)
2016/07/06 PHP
Laravel核心解读之异常处理的实践过程
2019/02/24 PHP
如何在标题栏显示框架内页面的标题
2007/02/03 Javascript
javascript 一个自定义长度的文本自动换行的函数
2007/08/19 Javascript
js对象之JS入门之Array对象操作小结
2011/01/09 Javascript
各浏览器中querySelector和querySelectorAll的实现差异分析
2012/05/23 Javascript
javascript跑马灯悬停放大效果实现代码
2012/12/12 Javascript
用javascript删除当前行,添加行(示例代码)
2013/11/25 Javascript
JS替换字符串中空格方法
2015/04/17 Javascript
轻量级的原生js日历插件calendar.js使用指南
2015/04/28 Javascript
jquery小火箭返回顶部代码分享
2015/08/19 Javascript
js 判断附件后缀的简单实现方法
2016/10/11 Javascript
Javascript 两种刷新方法以及区别和适用范围
2017/01/17 Javascript
canvas知识总结
2017/01/25 Javascript
js-FCC算法-No repeats please字符串的全排列(详解)
2017/05/02 Javascript
基于zepto.js实现手机相册功能
2017/07/11 Javascript
Vue组件之Tooltip的示例代码
2017/10/18 Javascript
jQuery实现碰到边缘反弹的动画效果
2018/02/24 jQuery
详解nodejs通过响应回写的方式渲染页面资源
2018/04/07 NodeJs
vue组件库的在线主题编辑器的实现思路
2020/04/03 Javascript
解决echarts echarts数据动态更新和dataZoom被重置问题
2020/07/20 Javascript
[00:32]2018DOTA2亚洲邀请赛VGJ.T出场
2018/04/03 DOTA
python 二分查找和快速排序实例详解
2017/10/13 Python
Python实现的列表排序、反转操作示例
2019/03/13 Python
Python CVXOPT模块安装及使用解析
2019/08/01 Python
PAUL HEWITT手表美国站:德国北部时尚生活配饰品牌,船锚元素
2017/11/18 全球购物
德国购买门票网站:ADticket.de
2019/10/31 全球购物
C#里面如何倒序排列一个数组的元素?
2013/06/21 面试题
一年级语文教学反思
2014/02/13 职场文书
应届大专毕业生自我鉴定
2014/04/08 职场文书
捐献物资倡议书范文
2014/05/19 职场文书
关于工作时间玩手机的检讨书
2014/09/18 职场文书
党的群众路线调研报告
2014/11/03 职场文书
学校三八妇女节活动总结
2015/02/06 职场文书
母亲去世追悼词
2015/06/23 职场文书