成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

fuchenxuan / 2455人閱讀

摘要:到此為止,我們就基本講清楚了中的中間件洋蔥模型是如何自動(dòng)執(zhí)行的。

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

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

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

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

關(guān)于koa中間件如何執(zhí)行,官網(wǎng)上有一個(gè)非常經(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)行這個(gè)例子,然后使用curl工具,運(yùn)行:

curl http://localhost:3000

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

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

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

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

2. koa中的generator和compose

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

var gen=function*(){
  console.log("begin!");
  //yield語句,在這里跳出,將控制權(quán)交給anotherfunc函數(shù)。
  yield anotherfunc;
  //下次回來時(shí)候從這里開始執(zhí)行
  console.log("end!");
}

var anotherfunc(){
  console.log("this is another function!");
}

var g=gen();
var another=g.next();  //"begin!"
//another是一個(gè)對(duì)象,其中value成員就是返回的anotherfunc函數(shù)
another.value();  //"this is another function!"
g.next();  //"end!";

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

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

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!";

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

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

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

使用我們自己寫的compose對(duì)上面一個(gè)例子改造,是的其更接近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中間件寫法的感覺了呢?但是目前,我們還是一步一步手動(dòng)的在執(zhí)行我們這個(gè)洋蔥模型,能否寫一個(gè)函數(shù),自動(dòng)的來執(zhí)行我們這個(gè)模型呢?

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

上面例子中,最后的代碼我們可以看出一個(gè)規(guī)律,基本就是外層的generator調(diào)用next方法把控制權(quán)交給內(nèi)層,內(nèi)層再繼續(xù)調(diào)用next把方法交給更里面的一層。整個(gè)流程可以用一個(gè)函數(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ù)接受一個(gè)generator,其內(nèi)部執(zhí)行其實(shí)就是我們上一個(gè)例子的精簡,使用遞歸的方法執(zhí)行。運(yùn)行這個(gè)例子,可以看到結(jié)果和我們上一個(gè)例子相同。

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

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

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

另外歡迎多多關(guān)注我的個(gè)人博客哦^_^ 會(huì)不定期更新我的技術(shù)文章~

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/80864.html

相關(guān)文章

  • 深入探析koa異步回調(diào)處理

    摘要:而之后,我們得到的是一個(gè)是一個(gè)對(duì)象,我們可以使用語句定義回調(diào)函數(shù),函數(shù)的內(nèi)容呢,則是將讀取到的返回給并繼續(xù)讓從斷點(diǎn)處執(zhí)行。 在上一篇中我們梳理了koa當(dāng)中中間件的洋蔥模型執(zhí)行原理,并實(shí)現(xiàn)了一個(gè)可以讓洋蔥模型自動(dòng)跑起來的流程管理函數(shù)。這一篇,我們再來研究一下koa當(dāng)中異步回調(diào)同步化寫法的原理,同樣的,我們也會(huì)實(shí)現(xiàn)一個(gè)管理函數(shù),是的我們能夠通過同步化的寫法來寫異步回調(diào)函數(shù)。 1. 回調(diào)金字...

    Drinkey 評(píng)論0 收藏0
  • 深入koa源碼(一):架構(gòu)設(shè)計(jì)

    摘要:本文來自心譚博客深入源碼架構(gòu)設(shè)計(jì)前端面試設(shè)計(jì)模式手冊教程實(shí)戰(zhàn)等更多專題,請(qǐng)來導(dǎo)航頁領(lǐng)取食用所有系列文章都放在了。歡迎交流和最近讀了的源碼,理清楚了架構(gòu)設(shè)計(jì)與用到的第三方庫。 本文來自《心譚博客·深入koa源碼:架構(gòu)設(shè)計(jì)》前端面試、設(shè)計(jì)模式手冊、Webpack4教程、NodeJs實(shí)戰(zhàn)等更多專題,請(qǐng)來導(dǎo)航頁領(lǐng)取食用所有系列文章都放在了Github。歡迎交流和Star ?? ヽ(°▽°)ノ ...

    blankyao 評(píng)論0 收藏0
  • 從Generator開始學(xué)習(xí)Koa

    摘要:需要說明的是,每次執(zhí)行完函數(shù)之后,都會(huì)返回一個(gè)對(duì)象這個(gè)返回值有兩個(gè)屬性和,對(duì)象通過這個(gè)返回值來告訴外界函數(shù)的執(zhí)行情況。函數(shù)的返回值變成這樣可以發(fā)現(xiàn)的值變?yōu)榱?,因?yàn)楹瘮?shù)已經(jīng)執(zhí)行完了。在規(guī)范中,新增了兩個(gè)協(xié)議可迭代協(xié)議和迭代器協(xié)議。 Koa是最近比較火的一款基于Node的web開發(fā)框架。說他是一個(gè)框架,其實(shí)他更像是一個(gè)函數(shù)庫,通過某種思想(或者說某種約定),將眾多的中間件聯(lián)系在一起,從而提...

    doodlewind 評(píng)論0 收藏0
  • 靜態(tài)分析安全測試(SAST)優(yōu)缺點(diǎn)探析

    摘要:許多公司都投資于或之類的靜態(tài)分析安全測試,解決方案。用靜態(tài)分析方法確保編程安全一書詳細(xì)描述了靜態(tài)分析技術(shù)的基本原理。博士將靜態(tài)分析無法找出的諸多安全問題歸為瑕疵,而非程序錯(cuò)誤。 靜態(tài)分析安全測試(SAST)是指不運(yùn)行被測程序本身,僅通過分析或者檢查源程序的語法、結(jié)構(gòu)、過程、接口等來檢查程序的正確性,那么采用靜分析安全測試的方法有什么優(yōu)缺點(diǎn)呢,且讓小編給你說道說道。 許多公司都投資于 H...

    codeKK 評(píng)論0 收藏0
  • 【全文】狼叔:如何正確的學(xué)習(xí)Node.js

    摘要:感謝大神的免費(fèi)的計(jì)算機(jī)編程類中文書籍收錄并推薦地址,以后在倉庫里更新地址,聲音版全文狼叔如何正確的學(xué)習(xí)簡介現(xiàn)在,越來越多的科技公司和開發(fā)者開始使用開發(fā)各種應(yīng)用。 說明 2017-12-14 我發(fā)了一篇文章《沒用過Node.js,就別瞎逼逼》是因?yàn)橛腥嗽谥跎虾贜ode.js。那篇文章的反響還是相當(dāng)不錯(cuò)的,甚至連著名的hax賀老都很認(rèn)同,下班時(shí)讀那篇文章,竟然坐車的還坐過站了。大家可以很...

    Edison 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<