理解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 相关文章推荐
JavaScript中的事件处理
Jan 16 Javascript
asp 取文本框名称代码
Dec 02 Javascript
javascript简易缓动插件(源码打包)
Feb 16 Javascript
JavaScript地图拖动功能SpryMap的简单实现
Jul 17 Javascript
js使用eval解析json实例与注意事项分享
Jan 18 Javascript
js变形金刚文字特效代码分享
Aug 20 Javascript
原生JS上传大文件显示进度条 php上传文件代码
Mar 27 Javascript
JavaScript代码调试方法实例小结
Jan 05 Javascript
JavaScript学习笔记之基于定时器实现图片无缝滚动功能详解
Jan 09 Javascript
Easyui 关闭jquery-easui tab标签页前触发事件的解决方法
Apr 28 jQuery
countUp.js实现数字滚动效果
Oct 18 Javascript
vue.js封装switch开关组件的操作
Oct 26 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
解决使用attachEvent函数时,this指向被绑定的元素的问题的方法
2007/08/13 Javascript
JS DOM 操作实现代码
2010/08/01 Javascript
初学Jquery插件制作 在SageCRM的查询屏幕隐藏部分行的功能
2011/12/26 Javascript
分享一个asp.net pager分页控件
2012/01/04 Javascript
JS随即打乱数组实现代码
2012/12/03 Javascript
SWFObject基本用法实例分析
2015/07/20 Javascript
微信小程序 教程之注册页面
2016/10/17 Javascript
jQuery Validate 校验多个相同name的方法
2017/05/18 jQuery
JS简单实现自定义右键菜单实例
2017/05/31 Javascript
JavaScript用200行代码制作打飞机小游戏实例
2017/06/21 Javascript
Node.js如何实现注册邮箱激活功能 (常见)
2017/07/23 Javascript
Vue数据监听方法watch的使用
2018/03/28 Javascript
jQuery实现导航样式布局操作示例【可自定义样式布局】
2018/07/24 jQuery
通过图带你深入了解vue的响应式原理
2019/06/21 Javascript
JS控制GIF图片的停止与显示
2019/10/24 Javascript
使用JS来动态操作css的几种方法
2019/12/18 Javascript
原生JS封装拖动验证滑块的实现代码示例
2020/06/01 Javascript
pymssql ntext字段调用问题解决方法
2008/12/17 Python
使用Python生成随机密码的示例分享
2016/02/18 Python
Python贪吃蛇游戏编写代码
2020/10/26 Python
python笔记:mysql、redis操作方法
2017/06/28 Python
Python装饰器原理与用法分析
2018/04/30 Python
Python实现将通信达.day文件读取为DataFrame
2018/12/22 Python
Python Pillow Image Invert
2019/01/22 Python
Opencv+Python实现图像运动模糊和高斯模糊的示例
2019/04/11 Python
python3检查字典传入函数键是否齐全的实例
2020/06/05 Python
Keras之fit_generator与train_on_batch用法
2020/06/17 Python
纯CSS实现菜单、导航栏的3D翻转动画效果
2014/04/23 HTML / CSS
MyBag中文网:英国著名的时尚包袋电商零售网站
2020/07/31 全球购物
高中生操行评语
2014/04/25 职场文书
初三学习计划书范文
2014/04/30 职场文书
签约仪式策划方案
2014/06/02 职场文书
2014年人力资源部工作总结
2014/11/19 职场文书
CSS3实现模糊背景的三种效果示例
2021/03/30 HTML / CSS
Python中常见的导入方式总结
2021/05/06 Python
聊一聊python常用的编程模块
2021/05/14 Python