最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入解析koa之中間件流程控制

 更新時間:2019年06月17日 09:39:47   作者:勇敢的半導(dǎo)體  
這篇文章主要介紹了深入解析koa之中間件流程控制,koa被認(rèn)為是第二代web后端開發(fā)框架,相比于前代express而言,其最大的特色無疑就是解決了回調(diào)金字塔的問題,讓異步的寫法更加的簡潔。,需要的朋友可以參考下

前言

koa被認(rèn)為是第二代web后端開發(fā)框架,相比于前代express而言,其最大的特色無疑就是解決了回調(diào)金字塔的問題,讓異步的寫法更加的簡潔。在使用koa的過程中,其實一直比較好奇koa內(nèi)部的實現(xiàn)機(jī)理。最近終于有空,比較深入的研究了一下koa一些原理,在這里會寫一系列文章來記錄一下我的學(xué)習(xí)心得和理解。

在我看來,koa最核心的函數(shù)是大名鼎鼎的co,koa正是基于這個函數(shù)實現(xiàn)了異步回調(diào)同步化,以及中間件流程控制。當(dāng)然在這篇文章中我并不會去分析co源碼,我打算在整個系列文章中,一步一步講解如何實現(xiàn)koa中間件的流程控制原理,koa的異步回調(diào)同步寫法實現(xiàn)原理,最后在理解這些的基礎(chǔ)上,實現(xiàn)一個簡單的類似co的函數(shù)。

本篇首先只談一談koa的中間件流程控制原理。

1. koa中間件執(zhí)行流程

關(guān)于koa中間件如何執(zhí)行,官網(wǎng)上有一個非常經(jīng)典的例子,有興趣的可以去看看,不過這里,我想把它修改的更簡單一點(diǎn):

var koa = require('koa');
var app = koa();
app.use(function*(next) {
console.log('begin middleware 1');
yield next;
console.log('end middleware 1');
});
app.use(function*(next) {
console.log('begin middleware 2');
yield next;
console.log('end middleware 2');
});
app.use(function*() {
console.log('middleware 3');
});
app.listen(3000);

運(yùn)行這個例子,然后使用curl工具,運(yùn)行:

curl http://localhost:3000

可以看到,運(yùn)行之后,會輸出:

begin middleware 1
begin middleware 2
middleware 3
end middleware 2
end middleware 1

這個例子非常形象的代表了koa的中間件執(zhí)行機(jī)制,可以用下圖的洋蔥模型來形容:

通過這種執(zhí)行流程,開發(fā)者可以非常方便的開發(fā)一些中間件,并且非常容易的整合到實際業(yè)務(wù)流程中。那么,這樣的流程又是如何實現(xiàn)和控制的呢?

2. koa中的generator和compose

簡單來說,洋蔥模型的執(zhí)行流程是通過es6中的generator來實現(xiàn)的。不熟悉generator的同學(xué)可以去看看其特性,其中一個就是generator函數(shù)可以像打斷點(diǎn)一樣從函數(shù)某個地方跳出,之后還可以再回來繼續(xù)執(zhí)行。下面一個例子可以說明這種特性:

var gen=function*(){
console.log('begin!');
//yield語句,在這里跳出,將控制權(quán)交給anotherfunc函數(shù)。
yield anotherfunc;
//下次回來時候從這里開始執(zhí)行
console.log('end!');
}
var anotherfunc(){
console.log('this is another function!');
}
var g=gen();
var another=g.next(); //'begin!'
//another是一個對象,其中value成員就是返回的anotherfunc函數(shù)
another.value(); //'this is another function!'
g.next(); //'end!';

從這個簡單例子中,可以看出洋蔥模型最基本的一個雛形,即yield前后的語句最先和最后執(zhí)行,yield中間的代碼在中心執(zhí)行。

現(xiàn)在設(shè)想一下,如果yield后面跟的函數(shù)本身就又是一個generator,會怎么樣呢?其實就是從上面例子里面做一個引申:

var gen1=function*(){
console.log('begin!');
yield g2;
console.log('end!');
}
var gen2=function*(){
console.log('begin 2');
yield anotherfunc;
console.log('end 2');
}
var anotherfunc(){
console.log('this is another function!');
}
var g=gen();
var g2=gen2();
var another1=g.next(); //'begin!';
var another2=another1.value.next(); //'begin 2';
another2.value(); //'this is another function!';
another1.value.next(); //'end 2';
g.next(); //'end!';

可以看出,基本上是用上面的例子,再加一個嵌套而已,原理是一樣的。

而在koa中,每個中間件generator都有一個next參數(shù)。在我們這個例子中,g2就可以看成是g函數(shù)的next參數(shù)。事實上,koa也確實是這樣做的,當(dāng)使用app.use()掛載了所有中間件之后,koa有一個koa-compose模塊,用于將所有g(shù)enerator中間件串聯(lián)起來,基本上就是將后一個generator賦給前一個generator的next參數(shù)。koa-compose的源碼非常簡單短小,下面是我自己實現(xiàn)的一個:

function compose(middlewares) {
return function(next) {
var i = middlewares.length;
var next = function*() {}();
while (i--) {
next = middlewares[i].call(this, next);
}
return next;
}
}

使用我們自己寫的compose對上面一個例子改造,是的其更接近koa的形式:

function compose(middlewares) {
return function(next) {
var i = middlewares.length;
var next = function*() {}();
while (i--) {
next = middlewares[i].call(this, next);
}
return next;
}
}
var gen1=function*(next){
console.log('begin!');
yield next;
console.log('end!');
}
var gen2=function*(next){
console.log('begin 2');
yield next;
console.log('end 2');
}
var gen3=function*(next){
console.log('this is another function!');
}
var bundle=compose([gen1,gen2,gen3]);
var g=bundle();
var another1=g.next(); //'begin!';
var another2=another1.value.next(); //'begin 2';
another2.value.next(); //'this is another function!';
another1.value.next(); //'end 2';
g.next(); //'end!';

怎么樣?是不是有一點(diǎn)koa中間件寫法的感覺了呢?但是目前,我們還是一步一步手動的在執(zhí)行我們這個洋蔥模型,能否寫一個函數(shù),自動的來執(zhí)行我們這個模型呢?

3. 讓洋蔥模型自動跑起來:一個run函數(shù)的編寫

上面例子中,最后的代碼我們可以看出一個規(guī)律,基本就是外層的generator調(diào)用next方法把控制權(quán)交給內(nèi)層,內(nèi)層再繼續(xù)調(diào)用next把方法交給更里面的一層。整個流程可以用一個函數(shù)嵌套的寫法寫出來。話不多說,直接上代碼:

function run(gen) {
var g;
if (typeof gen.next === 'function') {
g = gen;
} else {
g = gen();
}
function next() {
var tmp = g.next();
//如果tmp.done為true,那么證明generator執(zhí)行結(jié)束,返回。
if (tmp.done) {
return;
} else if (typeof g.next === 'function') {
run(tmp.value);
next();
}
}
next();
}
function compose(middlewares) {
return function(next) {
var i = middlewares.length;
var next = function*() {}();
while (i--) {
next = middlewares[i].call(this, next);
}
return next;
}
}
var gen1 = function*(next) {
console.log('begin!');
yield next;
console.log('end!');
}
var gen2 = function*(next) {
console.log('begin 2');
yield next;
console.log('end 2');
}
var gen3 = function*(next) {
console.log('this is another function!');
}
var bundle = compose([gen1, gen2, gen3]);
run(bundle);

run函數(shù)接受一個generator,其內(nèi)部執(zhí)行其實就是我們上一個例子的精簡,使用遞歸的方法執(zhí)行。運(yùn)行這個例子,可以看到結(jié)果和我們上一個例子相同。

到此為止,我們就基本講清楚了koa中的中間件洋蔥模型是如何自動執(zhí)行的。事實上,koa中使用的co函數(shù),一部分功能就是實現(xiàn)我們這里編寫的run函數(shù)的功能。

值得注意的是,這篇文章只注重分析中間件執(zhí)行流程的實現(xiàn),暫時并沒有考慮異步回調(diào)同步化原理。下一篇文章中,我將帶大家繼續(xù)探析koa中異步回調(diào)同步化寫法的機(jī)理。

這篇文章的代碼可以在github上面找到:https://github.com/mly-zju/async-js-demo,其中process_control.js文件就是本篇的事例源碼。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 使用socket.io制做簡易WEB聊天室

    使用socket.io制做簡易WEB聊天室

    這篇文章主要為大家詳細(xì)介紹了使用socket.io制做簡易WEB聊天室,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • 總結(jié)幾道關(guān)于Node.js的面試問題

    總結(jié)幾道關(guān)于Node.js的面試問題

    這篇文章主要總結(jié)了幾道關(guān)于Node.js的面試問題,通過這些問題就來判斷一個人的Node.js水平是不太嚴(yán)謹(jǐn)?shù)?,但是它能讓你對面試者在Node.js上的經(jīng)驗如何有個大概的了解。有需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-01-01
  • 10個Node.js庫幫助你優(yōu)化代碼和簡化開發(fā)

    10個Node.js庫幫助你優(yōu)化代碼和簡化開發(fā)

    這篇文章主要介紹了10個Node.js庫幫助你優(yōu)化代碼和簡化開發(fā),其中包括處理數(shù)組、對象、字符串庫Lodash,緩存數(shù)據(jù)處理庫Node-cache,解析、操作和格式化日期和時間庫Moment.js,Redis操作庫,發(fā)送電子郵件庫Nodemailer
    2023-05-05
  • 更新npm項目依賴的實現(xiàn)方法

    更新npm項目依賴的實現(xiàn)方法

    依賴管理是其中一個至關(guān)重要的環(huán)節(jié),本文主要介紹了更新npm項目依賴的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-06-06
  • npx的使用及原理分析

    npx的使用及原理分析

    這篇文章主要介紹了npx的使用及原理,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • Linux Centos7.2下安裝nodejs&npm配置全局路徑的教程

    Linux Centos7.2下安裝nodejs&npm配置全局路徑的教程

    今天小編就為大家分享一篇Linux Centos7.2下安裝nodejs&npm配置全局路徑的教程,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • Node.js基于cors解決接口跨域的問題(推薦)

    Node.js基于cors解決接口跨域的問題(推薦)

    這篇文章主要介紹了Node.js基于cors解決接口跨域的問題,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • nodejs+socket.io實現(xiàn)p2p消息實時發(fā)送的項目實踐

    nodejs+socket.io實現(xiàn)p2p消息實時發(fā)送的項目實踐

    本文主要介紹了nodejs+socket.io實現(xiàn)p2p消息實時發(fā)送,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Node.JS事件的綁定與觸發(fā)示例詳解

    Node.JS事件的綁定與觸發(fā)示例詳解

    Node中的事件模型就是我們常見的訂閱發(fā)布模式,Nodejs核心API都采用異步事件驅(qū)動,所有可能觸發(fā)事件的對象都是一個繼承自EventEmitter類的子類實例對象,這篇文章主要給大家介紹了關(guān)于Node.JS事件的綁定與觸發(fā)事件的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • nodejs處理http請求實例詳解之get和post

    nodejs處理http請求實例詳解之get和post

    最近一段時間在學(xué)習(xí)前端向服務(wù)器發(fā)送數(shù)據(jù)和請求數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于nodejs處理http請求實例詳解之get和post的相關(guān)資料,需要的朋友可以參考下
    2023-01-01

最新評論

临邑县| 惠来县| 阿拉善左旗| 定远县| 桃园县| 新民市| 清原| 睢宁县| 遵义县| 商水县| 大理市| 勐海县| 东乌| 威海市| 东方市| 桑植县| 佛坪县| 怀安县| 临海市| 喀什市| 昭通市| 玉屏| 阿荣旗| 乌鲁木齐市| 闻喜县| 兴城市| 海阳市| 大化| 长寿区| 工布江达县| 海原县| 日土县| 灵川县| 焦作市| 宜春市| 咸宁市| 郴州市| 府谷县| 嘉荫县| 辽阳县| 贵德县|