理解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 函数调用规则
Sep 14 Javascript
javascript代码加载优化方法
Jan 30 Javascript
关于js日期转化为毫秒数“节省20%的效率和和节省9个字符“问题
Mar 01 Javascript
Extjs4 关于Store的一些操作(加载/回调/添加)
Apr 18 Javascript
ExtJS实现文件下载的方法实例
Nov 09 Javascript
Extjs的FileUploadField文件上传出现了两个上传按钮
Apr 29 Javascript
JS实现的论坛Ajax打分效果完整实例
Oct 31 Javascript
从0开始学Vue
Oct 27 Javascript
BetterScroll 在移动端滚动场景的应用
Sep 18 Javascript
webpack自动打包和热更新的实现方法
Jun 24 Javascript
JS实现百度搜索框关键字推荐
Feb 17 Javascript
JS数组的高级使用方法示例小结
Mar 14 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
山进SANGEAN ATS-909X电路分析
2021/03/02 无线电
PHP笔记之:基于面向对象设计的详解
2013/05/14 PHP
THINKPHP2.0到3.0有哪些改进之处
2015/01/04 PHP
PHP7新特性foreach 修改示例介绍
2016/08/26 PHP
PHP无限极分类函数的实现方法详解
2017/04/15 PHP
Yii2框架操作数据库的方法分析【以mysql为例】
2019/05/27 PHP
基于Jquery的文字滚动跑马灯插件(一个页面多个滚动区)
2010/07/26 Javascript
javascript 事件绑定问题
2011/01/01 Javascript
简单的邮箱登陆的提示效果类似于yahoo邮箱
2014/02/26 Javascript
D3.js 从P元素的创建开始(显示可加载数据)
2014/10/30 Javascript
jQuery获得document和window对象宽度和高度的方法
2015/03/25 Javascript
JavaScript对HTML DOM使用EventListener进行操作
2015/10/21 Javascript
浅析如何利用angular结合translate为项目实现国际化
2016/12/08 Javascript
js实现瀑布流效果(自动生成新的内容)
2017/03/16 Javascript
Node.js dgram模块实现UDP通信示例代码
2017/09/26 Javascript
jQuery+CSS实现的table表格行列转置功能示例
2018/01/08 jQuery
微信小程序使用canvas自适应屏幕画海报并保存图片功能
2019/07/25 Javascript
nodejs对mongodb数据库的增加修删该查实例代码
2020/01/05 NodeJs
解决vue+router路由跳转不起作用的一项原因
2020/07/19 Javascript
原生js生成图片验证码
2020/10/11 Javascript
基于python的汉字转GBK码实现代码
2012/02/19 Python
python daemon守护进程实现
2016/08/27 Python
Python编写登陆接口的方法
2017/07/10 Python
Python实现拷贝/删除文件夹的方法详解
2018/08/29 Python
对Python3之进程池与回调函数的实例详解
2019/01/22 Python
Python+OpenCV图片局部区域像素值处理详解
2019/01/23 Python
Python实现爬取网页中动态加载的数据
2020/08/17 Python
html5 拖拽及用 js 实现拖拽功能的示例代码
2020/10/23 HTML / CSS
荷兰最大的鞋子、服装和运动折扣店:Bristol
2021/01/07 全球购物
设计总监岗位职责
2013/12/07 职场文书
消防战士优秀事迹材料
2014/02/13 职场文书
白血病捐款倡议书
2014/05/14 职场文书
企业形象策划方案
2014/05/29 职场文书
护理专业自荐书
2014/06/04 职场文书
幼儿学前班评语
2014/12/29 职场文书
宿舍卫生管理制度
2015/08/05 职场文书