热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

你晓得koa中间件实行道理吗

媒介原文地点近来几天花了比较长的时刻在koa(1)的源码剖析上面,首次看的时刻,被中间件实行那段整的晕乎乎的,完整不晓得所以,再次看,彷佛邃晓了些什么,再重复看,我去,险些神了,险

媒介

原文地点

近来几天花了比较长的时刻在koa(1)的源码剖析上面,首次看的时刻,被中间件实行那段整的晕乎乎的,完整不晓得所以,再次看,彷佛邃晓了些什么,再重复看,我去,险些神了,险些泣如雨下,险些丧尽天良啊!!!

《你晓得koa中间件实行道理吗》

用在前面

下面的例子会在掌握台中打印出一些信息(详细打印出什么?能够猜猜?),然后返回hello world

let koa = require('koa')
let app = koa()
app.use(function * (next) {
console.log('generate1----start')
yield next
console.log('generate1----end')
})
app.use(function * (next) {
console.log('generate2----start')
yield next
console.log('generate2----end')
this.body = 'hello world'
})
app.listen(3000)

用过koa的同砚都晓得增加中间件的体式格局是运用koa实例的use要领,并传入一个generator函数,这个generator函数能够接收一个next(这个next究竟是啥?这里先不申明,在背面会细致申明)。

实行use干了嘛

这是koa的组织函数,为了没有其他信息的滋扰,我去除了一些临时用不到的代码,这里我们把目光聚焦在middleware这个数组即可。

function Application() {
// xxx
this.middleware = []; // 这个数组就是用来装一个个中间件的
// xxx
}

接下来我们要看use要领了

一样去除了一些临时不必的代码,能够看到每次实行use要领,就把表面传进来的generator函数push到middleware数组中


app.use = function(fn){
// xxx
this.middleware.push(fn);
// xxx
};

好啦!你已晓得koa中是预先经由历程use要领,将要求可能会经由的中间件装在了一个数组中。

接下来我们要最先本文的重点了,当一个要求到来的时刻,是怎样经由中间件,怎样跑起来的

起首我们只需晓得下面这段callback函数就是要求到来的时刻实行的回调即可(一样只管去除了我们不必的代码)


app.callback = function(){
// xxx
var fn = this.experimental
? compose_es7(this.middleware)
: co.wrap(compose(this.middleware));
// xxx
return function(req, res){
// xxx
fn.call(ctx).then(function () {
respond.call(ctx);
}).catch(ctx.onerror);
// xxx
}
};

这段代码能够分红两个部份

  1. 要求前的中间件初始化处置惩罚部份

  2. 要求到来时的中间件运转部份

我们分部份来讲一下


var fn = this.experimental
? compose_es7(this.middleware)
: co.wrap(compose(this.middleware));

这段代码对experimental做了下推断,假如设置为了true那末koa中将能够支撑传入async函数,不然就实行co.wrap(compose(this.middleware))

只要一行初始化中间件就做完啦?

《你晓得koa中间件实行道理吗》

我晓得koa很屌,但也别这么屌好不好,所以说评价一个好的程序员不是由代码量决议的

我们来看下这段代码到底有什么奇异的处所

compose(this.middleware)

把装着中间件middleware的数组作为参数传进了compose这个要领,那末compose做了什么事呢?实在就是把底本毫无关联的一个个中间件给首尾串起来了,因而他们之间就有了千丝万缕的联络。

function compose(middleware){
return function *(next){
// 第一次获得next是由于*noop天生的generator对象
if (!next) next = noop();
var i = middleware.length;
// 从后往前最先实行middleware中的generator函数
while (i--) {
// 把后一个中间件获得的generator对象传给前一个作为第一个参数存在
next = middleware[i].call(this, next);
} return yield *next;
}
}
function *noop(){}

笔墨解释一下就是,compose将中间件从末了一个最先处置惩罚,并一向往前直到第一个中间件。个中异常症结的就是将后一个中间件获得generator对象作为参数(这个参数就是文章开首说到的next啦,也就是说next实际上是一个generator对象)传给前一个中间件。固然末了一个中间件的参数next是一个空的generator函数天生的对象。

我们本身来写一个简朴的例子申明compose是怎样将多个generator函数串连起来的

function * gen1 (next) {
yield 'gen1'
yield * next // 最先实行下一个中间件
yield 'gen1-end' // 下一个中间件实行完成再继承实行gen1中间件的逻辑
}
function * gen2 (next) {
yield 'gen2'
yield * next // 最先实行下一个中间件
yield 'gen2-end' // 下一个中间件实行完成再继承实行gen2中间件的逻辑
}
function * gen3 (next) {
yield 'gen3'
yield * next // 最先实行下一个中间件
yield 'gen3-end' // 下一个中间件实行完成再继承实行gen3中间件的逻辑
}
function * noop () {}
var middleware = [gen1, gen2, gen3]
var len = middleware.length
var next = noop() // 提供给末了一个中间件的参数
while(len--) {
next = middleware[len].call(null, next)
}
function * letGo (next) {
yield * next
}
var g = letGo(next)
g.next() // {value: "gen1", done: false}
g.next() // {value: "gen2", done: false}
g.next() // {value: "gen3", done: false}
g.next() // {value: "gen3-end", done: false}
g.next() // {value: "gen2-end", done: false}
g.next() // {value: "gen1-end", done: false}
g.next() // {value: undefined, done: true}

看到了吗?中间件被串起来以后实行的递次是

gen1 -> gen2 -> gen3 -> noop -> gen3 -> gen2 -> gen1

从而首尾相连,进而发生了关联?。

co.wrap

经由历程compose处置惩罚后返回了一个generator函数。

co.wrap(compose(this.middleware))

一切上述代码能够明白为

co.wrap(function * gen ())

好,我们再看看co.wrap做了什么,逐步地一步步靠近了哦

co.wrap = function (fn) {
createPromise.__generatorFunction__ = fn;
return createPromise;
function createPromise() {
return co.call(this, fn.apply(this, arguments));
}
}

能够看到co.wrap返回了一个一般函数createPromise,这个函数就是文章开首的fn啦。

var fn = this.experimental
? compose_es7(this.middleware)
: co.wrap(compose(this.middleware));

中间件最先跑起来啦

前面已说完了,中间件是怎样初始化的,即假如由不相干到关联密切了,接下来最先说要求到来时,初始化好的中间件是怎样跑的。

fn.call(ctx).then(function () {
respond.call(ctx);
}).catch(ctx.onerror);

这一段就是要求到来手即将要经由的中间件实行部份,fn实行以后返回的是一个Promise,koa经由历程注册胜利和失利的回调函数来离别处置惩罚要求。

让我们回到

co.wrap = function (fn) {
// xxx
function createPromise() {
return co.call(this, fn.apply(this, arguments));
}
}

createPromise内里的fn就是经由compose处置惩罚中间件后返回的一个generator函数,那末实行以后拿到的就是一个generator对象了,并把这个对象传经典范的co内里啦。假如你需要对co的源码相识迎接检察昨天写的走一步再走一步,揭开co的神奇面纱,好了,接下来就是看co内里怎样处置惩罚这个被compose处置惩罚过的generator对象了

再回忆一下co


function co(gen) {
var ctx = this;
var args = slice.call(arguments, 1)
// we wrap everything in a promise to avoid promise chaining,
// which leads to memory leak errors.
// see https://github.com/tj/co/issues/180
return new Promise(function(resolve, reject) {
if (typeof gen === 'function') gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== 'function') return resolve(gen);
onFulfilled();
/**
* @param {Mixed} res
* @return {Promise}
* @api private
*/
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}
/**
* @param {Error} err
* @return {Promise}
* @api private
*/
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
/**
* Get the next value in the generator,
* return a promise.
*
* @param {Object} ret
* @return {Promise}
* @api private
*/
function next(ret) {
if (ret.done) return resolve(ret.value);
var value = toPromise.call(ctx, ret.value);
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
}
});
}

我们直接看一下onFulfilled,这个时刻第一次进co的时刻由于已是generator对象所以会直接实行onFulfilled()

function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}

gen.next恰是用于去实行中间件的营业逻辑,当碰到yield语句的时刻,将紧随厥后的效果返回赋值给ret,一般这里的ret,就是我们文中说道的next,也就是当前中间件的下一个中间件。

拿到下一个中间件后把他交给next去处置惩罚

function next(ret) {
if (ret.done) return resolve(ret.value);
var value = toPromise.call(ctx, ret.value);
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
}

当中间件实行完毕了,就把Promise的状况设置为胜利。不然就将ret(也就是下一个中间件)再用co包一次。主要看toPromise的这几行代码即可


function toPromise(obj) {
// xxx
if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
// xxx
}

注重噢toPromise这个时刻的返回值是一个Promise,这个异常症结,是下一个中间件实行完成以后回溯到上一个中间件中缀实行处继承实行的症结

function next(ret) {
// xxx
var value = toPromise.call(ctx, ret.value);
// 即经由历程前面toPromise返回的Promise完成,当后一个中间件实行完毕,回退到上一个中间件中缀处继承实行
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
// xxx
}

看到这里,我们能够总结出,险些koa的中间件都会被co给包装一次,而每个中间件又能够经由历程Promise的then去监测厥后一个中间件是不是完毕,后一个中间件完毕后会实行前一个中间件用then监听的操纵,这个操纵就是实行该中间件yield next背面的那些代码

打个比如:

当koa中接收到一个要求的时刻,要求将经由两个中间件,离别是中间件1中间件2

中间件1

// 中间件1在yield 中间件2之前的代码
yield 中间件2
// 中间件2实行完成以后继承实行中间件1的代码

中间件2

// 中间件2在yield noop中间件之前的代码
yield noop中间件
// noop中间件实行完成以后继承实行中间件2的代码

那末处置惩罚的历程就是co会立时挪用onFulfilled来实行中间件1前半部份代码,碰到yield 中间件2的时刻获得中间件2generator对象,紧接着,又把这个对象放到co内里继承实行一遍,以此类推下去晓得末了一个中间件(我们这里的指的是谁人空的noop中间件)实行完毕,继而立时挪用promise的resolve要领示意完毕,ok,这个时刻中间件2监听到noop实行完毕了,立时又去实行了onFulfilled来实行yield noop中间件后半部份代码,好啦这个时刻中间件2也实行完毕了,也会立时挪用promise的resolve要领示意完毕,ok,这个时刻中间件1监听到中间件2实行完毕了,立时又去实行了onFulfilled来实行yield 中间件2后半部份代码,末了中间件悉数实行完了,就实行respond.call(ctx);

啊 啊 啊好绕,不过逐步看,细致想,照样能够想邃晓的。用代码示意这个历程有点相似

new Promise((resolve, reject) => {
// 我是中间件1
yield new Promise((resolve, reject) => {
// 我是中间件2
yield new Promise((resolve, reject) => {
// 我是body
})
// 我是中间件2
})
// 我是中间件1
});

《你晓得koa中间件实行道理吗》

末端

罗里吧嗦说了一大堆,也不晓得有无把实行道理说邃晓。

假如对你明白koa有些许协助,不介意的话,点击源码地点点颗小星星吧

假如对你明白koa有些许协助,不介意的话,点击源码地点点颗小星星吧

假如对你明白koa有些许协助,不介意的话,点击源码地点点颗小星星吧

源码地点


推荐阅读
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 20211101CleverTap参与度和分析工具功能平台学习/实践
    1.应用场景主要用于学习CleverTap的使用,该平台主要用于客户保留与参与平台.为客户提供价值.这里接触到的原因,是目前公司用到该平台的服务~2.学习操作 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 本文介绍了一个适用于PHP应用快速接入TRX和TRC20数字资产的开发包,该开发包支持使用自有Tron区块链节点的应用场景,也支持基于Tron官方公共API服务的轻量级部署场景。提供的功能包括生成地址、验证地址、查询余额、交易转账、查询最新区块和查询交易信息等。详细信息可参考tron-php的Github地址:https://github.com/Fenguoz/tron-php。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
author-avatar
只是遇不到他_740
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有