傻瓜式解读koa中间件处理模块koa-compose的使用


Posted in Javascript onOctober 30, 2018

最近需要单独使用到koa-compose这个模块,虽然使用koa的时候大致知道中间件的执行流程,但是没仔细研究过源码用起来还是不放心(主要是这个模块代码少,多的话也没兴趣去研究了)。

koa-compose看起来代码少,但是确实绕。闭包,递归,Promise。。。看了一遍脑子里绕不清楚。看了网上几篇解读文章,都是针对单行代码做解释,还是绕不清楚。最后只好采取一种傻瓜的方式:

koa-compose去掉一些注释,类型校验后,源码如下:

function compose (middleware) {
 return function (context, next) {
  // last called middleware #
  let index = -1
  return dispatch(0)
  function dispatch (i) {
   if (i <= index) return Promise.reject(new Error('next() called multiple times'))
   index = i
   let fn = middleware[i]
   if (i === middleware.length) fn = next
   if (!fn) return Promise.resolve()
   try {
    return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
   } catch (err) {
    return Promise.reject(err)
   }
  }
 }
}

写出如下代码:

var index = -1;
function compose() {
  return dispatch(0)
}
function dispatch (i) {
   if (i <= index) return Promise.reject(new Error('next() called multiple times'))
   index = i
   var fn = middleware[i]
   if (i === middleware.length) fn = next
   if (!fn) return Promise.resolve('fn is undefined')
   try {
    return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
   } catch (err) {
    return Promise.reject(err)
   }
 }
 
 function f1(context,next){
  console.log('middleware 1');
  next().then(data=>console.log(data));
  console.log('middleware 1');
  return 'middleware 1 return';
 }
 function f2(context,next){
  console.log('middleware 2');
  next().then(data=>console.log(data));
  console.log('middleware 2');
  return 'middleware 2 return';
 }
 function f3(context,next){
  console.log('middleware 3');
  next().then(data=>console.log(data));
  console.log('middleware 3');
  return 'middleware 3 return';
 }
var middleware=[
 f1,f2,f3
]

var context={};
var next=function(context,next){
  console.log('middleware 4');
  next().then(data=>console.log(data));
  console.log('middleware 4');
  return 'middleware 4 return';
};
compose().then(data=>console.log(data));

直接运行结果如下:

"middleware 1"

"middleware 2"

"middleware 3"

"middleware 4"

"middleware 4"

"middleware 3"

"middleware 2"

"middleware 1"

"fn is undefined"

"middleware 4 return"

"middleware 3 return"

"middleware 2 return"

"middleware 1 return"

按着代码运行流程一步步分析:

dispatch(0)

i==0,index==-1 i>index 往下

index=0

fn=f1

Promise.resolve(f1(context, dispatch.bind(null, 0 + 1)))

这就会执行

f1(context, dispatch.bind(null, 0 + 1))

进入到f1执行上下文

console.log('middleware 1');

输出middleware 1

next()

其实就是调用dispatch(1) bind的功劳

递归开始

dispatch(1)

i==1,index==0 i>index 往下

index=1

fn=f2

Promise.resolve(f2(context, dispatch.bind(null, 1 + 1)))

这就会执行

f2(context, dispatch.bind(null, 1 + 1))

进入到f2执行上下文

console.log('middleware 2');

输出middleware 2

next()

其实就是调用dispatch(2)

接着递归

dispatch(2)

i==2,index==1 i>index 往下

index=2

fn=f3

Promise.resolve(f3(context, dispatch.bind(null, 2 + 1)))

这就会执行

f3(context, dispatch.bind(null, 2 + 1))

进入到f3执行上下文

console.log('middleware 3');

输出middleware 3

next()

其实就是调用dispatch(3)

接着递归

dispatch(3)

i==3,index==2 i>index 往下

index=3

i === middleware.length

fn=next

Promise.resolve(next(context, dispatch.bind(null, 3 + 1)))

这就会执行

next(context, dispatch.bind(null, 3 + 1))

进入到next执行上下文

console.log('middleware 4');

输出middleware 4

next()

其实就是调用dispatch(4)

接着递归

dispatch(4)

i==4,index==3 i>index 往下

index=4

fn=middleware[4]

fn=undefined

reuturn Promise.resolve('fn is undefined')

回到next执行上下文

console.log('middleware 4');

输出middleware 4

return 'middleware 4 return'
Promise.resolve('middleware 4 return')

回到f3执行上下文

console.log('middleware 3');

输出middleware 3

return 'middleware 3 return'
Promise.resolve('middleware 3 return')

回到f2执行上下文

console.log('middleware 2');

输出middleware 2

return 'middleware 2 return'
Promise.resolve('middleware 2 return')

回到f1执行上下文

console.log('middleware 1');

输出middleware 1

return 'middleware 1 return'
Promise.resolve('middleware 1 return')

回到全局上下文

至此已经输出

"middleware 1"

"middleware 2"

"middleware 3"

"middleware 4"

"middleware 4"

"middleware 3"

"middleware 2"

"middleware 1"

那么

"fn is undefined"

"middleware 4 return"

"middleware 3 return"

"middleware 2 return"

"middleware 1 return"

怎么来的呢

回头看一下,每个中间件里都有

next().then(data=>console.log(data));

按照之前的分析,then里最先拿到结果的应该是next中间件的,而且结果就是Promise.resolve('fn is undefined')的结果,然后分别是f4,f3,f2,f1。那么为什么都是最后才输出呢?

Promise.resolve('fn is undefined').then(data=>console.log(data));
console.log('middleware 4');

运行一下就清楚了

或者

setTimeout(()=>console.log('fn is undefined'),0);
console.log('middleware 4');

整个调用过程还可以看成是这样的:

function composeDetail(){
 return Promise.resolve(
  f1(context,function(){
   return Promise.resolve(
    f2(context,function(){
     return Promise.resolve(
      f3(context,function(){
       return Promise.resolve(
        next(context,function(){
         return Promise.resolve('fn is undefined')
        })
       )
      })
     )
    })
   )
  })
 )
}
composeDetail().then(data=>console.log(data));

方法虽蠢,但是compose的作用不言而喻了

最后,if (i <= index) return Promise.reject(new Error('next() called multiple times'))这句代码何时回其作用呢?

一个中间件里调用两次next(),按照上面的套路走,相信很快就明白了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js 解决“options为空或不是对象”
Dec 22 Javascript
JavaScript对象和字串之间的转换实例探讨
Apr 21 Javascript
jquery自动填充勾选框即把勾选框打上true
Mar 24 Javascript
Jquery选择器中使用变量实现动态选择例子
Jul 25 Javascript
js获得当前系统日期时间的方法
May 06 Javascript
AngularJS 模块详解及简单实例
Jul 28 Javascript
Angularjs结合Bootstrap制作的一个TODO List
Aug 18 Javascript
JS中mouseup事件丢失的原因与解决办法
Jun 14 Javascript
layer.confirm取消按钮绑定事件的方法
Aug 17 Javascript
Vue 重置组件到初始状态的方法示例
Oct 10 Javascript
js实现多张图片每隔一秒切换一张图片
Jul 29 Javascript
jquery插件实现代码雨特效
Apr 24 jQuery
微信小程序实现单选功能
Oct 30 #Javascript
基于vue2.0实现仿百度前端分页效果附实现代码
Oct 30 #Javascript
小程序实现多选框功能
Oct 30 #Javascript
vue-cli项目配置多环境的详细操作过程
Oct 30 #Javascript
详解微信小程序中组件通讯
Oct 30 #Javascript
vue移动端项目缓存问题实践记录
Oct 29 #Javascript
vue 使用vue-i18n做全局中英文切换的方法
Oct 29 #Javascript
You might like
微信开发之网页授权获取用户信息(二)
2016/01/08 PHP
PHP实现的二分查找算法实例分析
2017/12/19 PHP
PHP实现的无限分类类库定义与用法示例【基于thinkPHP】
2018/08/06 PHP
PHP封装的page分页类定义与用法完整示例
2018/12/24 PHP
Yii框架页面渲染操作实例详解
2019/07/19 PHP
javascript操作文本框readOnly
2007/05/15 Javascript
Riot.js 快速的JavaScript单元测试框架
2009/11/09 Javascript
JavaScript四种调用模式和this示例介绍
2014/01/02 Javascript
JavaScript必知必会(十) call apply bind的用法说明
2016/06/08 Javascript
JavaScript SHA1加密算法实现详细代码
2016/10/06 Javascript
JavaScript获取短信验证码(周期性)
2016/12/29 Javascript
VUE axios发送跨域请求需要注意的问题
2017/07/06 Javascript
JS库之Waypoints的用法详解
2017/09/13 Javascript
JS异步执行结果获取的3种解决方式
2019/02/19 Javascript
react 原生实现头像滚动播放的示例
2020/04/21 Javascript
详解微信小程序「渲染层网络层错误」的解决方法
2021/01/06 Javascript
Vue 数据响应式相关总结
2021/01/28 Vue.js
用Python制作检测Linux运行信息的工具的教程
2015/04/01 Python
python处理二进制数据的方法
2015/06/03 Python
详解如何用OpenCV + Python 实现人脸识别
2017/10/20 Python
Python标准库inspect的具体使用方法
2017/12/06 Python
Sublime开发python程序的示例代码
2018/01/24 Python
python获取url的返回信息方法
2018/12/17 Python
windows系统中Python多版本与jupyter notebook使用虚拟环境的过程
2019/05/15 Python
纯python进行矩阵的相乘运算的方法示例
2019/07/17 Python
手把手教你安装Windows版本的Tensorflow
2020/03/26 Python
python-地图可视化组件folium的操作
2020/12/14 Python
python3中celery异步框架简单使用+守护进程方式启动
2021/01/20 Python
详解python第三方库的安装、PyInstaller库、random库
2021/03/03 Python
HTML5中的拖放实现详解
2017/08/23 HTML / CSS
公司年会演讲稿范文
2014/01/11 职场文书
劳动工资科岗位职责范本
2014/03/02 职场文书
2015年教师党员公开承诺书
2015/01/22 职场文书
关于迟到的检讨书
2015/05/06 职场文书
教你使用Python pypinyin库实现汉字转拼音
2021/05/27 Python
教你使用Jenkins集成Harbor自动发布镜像
2022/04/03 Servers