国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

通過實例分析javascript中的“中間件”

zhangyucha0 / 3110人閱讀

摘要:如果驗證沒出現(xiàn)問題,就注冊這個中間件并放到中間件數(shù)組中。但如果不執(zhí)行,中間件的處理也會終止。整理下流程默認會執(zhí)行中間件數(shù)組中的第一個,也就是代碼中的,第一個中間件通過返回的是第二個中間件的執(zhí)行。

介紹

如果你使用過redux或者nodejs,那么你對“中間件”這個詞一定不會感到陌生,如果沒用過這些也沒關系,也可以通過這個來了解javascript中的事件流程。

一個例子

有一類人,非常的懶(比如說我),只有三種行為動作,sleep,eat,sleepFirst,偽代碼就是:

var wang = new LazyMan("王大錘");
wang.eat("蘋果").eat("香蕉").sleep(5).eat("葡糖").eat("橘子").sleepFirst(2);
//等同于以下的代碼
const wang = new LazyMan("王大錘");
wang.eat("蘋果");
wang.eat("香蕉");
wang.sleep(5);
wang.eat("葡糖");
wang.eat("橘子");
wang.sleepFirst(2);

執(zhí)行結果如下圖:


不管什么,先睡2S


然后做個介紹,吃東西,睡5S


醒來,吃

但是javascript只有一個線程,也并沒有像php的sleep的那種方法。實現(xiàn)的思路就是eat、sleep、sleepFirst這些事件放在任務列中,通過next去依次執(zhí)行方法。我還是希望在看源碼前先手動實現(xiàn)一下試試看,其實這就是個lazyMan的實現(xiàn)。

下面是我的實現(xiàn)方式:

class lazyMan{
    constructor(name) {
        this.tasks = [];
        const first = () => {
            console.log(`my name is ${name}`);
            this.next();
        }
        this.tasks.push(first);
        setTimeout(()=>this.next(), 0);
    }
    next() {
        const task = this.tasks.shift();
        task && task();
    }
    eat(food) {
        const eat = () => {
            console.log(`eat ${food}`);
            this.next();
        };
        this.tasks.push(eat);
        return this;
    }
    sleep(time) {
        const newTime = time * 1000;
        const sleep = () => {
            console.log(`sleep ${time}s!`);
            setTimeout(() => {
                this.next();
            }, newTime);
        };
        this.tasks.push(sleep);
        return this;
    }
    sleepFirst(time) {
        const newTime = time * 1000;
        const sleepzFirst = () => {
            console.log(`sleep ${time}s first!`);
            setTimeout(() => {
                this.next();
            }, newTime);
        };
        this.tasks.unshift(sleepzFirst);
        return this;
    }
}
const aLazy = new lazyMan("王大錘");
aLazy.eat("蘋果").eat("香蕉").sleep(5).eat("葡萄").eat("橘子").sleepFirst(2)

我們上面說過

wang.eat("蘋果").eat("香蕉").sleep(5).eat("葡糖").eat("橘子").sleepFirst(2);
//等同于以下的代碼
wang.eat("蘋果");
wang.eat("香蕉");
wang.sleep(5);
wang.eat("葡糖");
wang.eat("橘子");
wang.sleepFirst(2);

如果你使用過過node,你會發(fā)現(xiàn),這種寫法似乎有點熟悉的感覺,我們來看一下一個koa2(一個node的框架)項目的主文件:

const Koa = require("koa");
const bodyParser = require("koa-bodyparser");
const cors = require("koa-cors2");

const routers = require("./src/routers/index")

const app = new Koa();

app.use(cors());
app.use(bodyParser());
app.use(routers.routes()).use(routers.allowedMethods())

app.listen(3000);

有沒有發(fā)現(xiàn)結構有一點像?

koa中的中間件

廢話不多說,直接看源碼...
app.use就是用來注冊中間件的,我們先看use的實現(xiàn):

 use(fn) {
    if (typeof fn !== "function") throw new TypeError("middleware must be a function!");
    if (isGeneratorFunction(fn)) {
      deprecate("Support for generators will be removed in v3. " +
                "See the documentation for examples of how to convert old middleware " +
                "https://github.com/koajs/koa/blob/master/docs/migration.md");
      fn = convert(fn);
    }
    debug("use %s", fn._name || fn.name || "-");
    this.middleware.push(fn);
    return this;
  }

先解釋一下里面做了什么處理,fn就是傳入的函數(shù),首先肯定要判斷是否是個函數(shù),如果不是,拋出錯誤,其次是判斷fn是否是一個GeneratorFunction,我用的是koa2,koa2中用asyncawait來替代koa1中的generator,如果判斷是生成器函數(shù),證明使用或者書寫的中間件為koa1的,koa2中提供了庫koa-convert來幫你把koa1中的中間件轉換為koa2中的中間件,這里如果判斷出是koa1的中間件會給你提醒,這里會主動幫你轉換,就是代碼中的convert方法。如果驗證沒出現(xiàn)問題,就注冊這個中間件并放到中間件數(shù)組中。
這里我們只看到了把中間件加到數(shù)組中,然后就沒有做其他處理了。
我們再看koa2中listen

  listen(...args) {
    debug("listen");
    const server = http.createServer(this.callback());
    return server.listen(...args);
  }

這里只是啟動了個server,然后傳進了一個回調函數(shù)的結果,我們看原生啟動一個server大概是什么樣的:

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("hello world
");
}).listen(3000);

原生的回調函數(shù)接受兩個參數(shù),一個是request一個是response,我們再去看koa2中這個回調函數(shù)的代碼:

callback() {
    const fn = compose(this.middleware);

    if (!this.listeners("error").length) this.on("error", this.onerror);

    const handleRequest = (req, res) => {
      res.statusCode = 404;
      const ctx = this.createContext(req, res);
      const onerror = err => ctx.onerror(err);
      const handleResponse = () => respond(ctx);
      onFinished(res, onerror);
      return fn(ctx).then(handleResponse).catch(onerror);
    };

    return handleRequest;
  }

這里有一個const fn = compose(this.middleware);compose這種不知道大家用的多不多,compose是函數(shù)式編程中使用比較多的東西,這里將多個中間件組合起來。
我們去看compose的實現(xiàn):

function compose (middleware) {
  if (!Array.isArray(middleware)) throw new TypeError("Middleware stack must be an array!")
  for (const fn of middleware) {
    if (typeof fn !== "function") throw new TypeError("Middleware must be composed of functions!")
  }

  /**
   * @param {Object} context
   * @return {Promise}
   * @api public
   */

  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, function next () {
          return dispatch(i + 1)
        }))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

首先判斷是否是中間件數(shù)組,這個不用多說,for...of是ES6中的新特性,這里不做說明,需要注意的是,數(shù)組和Set集合默認的迭代器是values()方法,Map默認的是entries()方法。

這里的dispatch和next一樣是所有的中間件的核心,dispatch的參數(shù)i其實也就是對應中間件的下標,,在第一次調用的時候傳入了參數(shù)0,如果中間件存在返回Promise

return Promise.resolve(fn(context, function next () {
  return dispatch(i + 1)
}))

我們lazyMan鏈式調用時不斷的shift()取出下一個要執(zhí)行的事件函數(shù),koa2里采用的是通過數(shù)組下標的方式找到下一個中間件,這里是用Promise.resolve包起來就達到了每一個中間件await next()返回的結果都剛好是下一個中間件的執(zhí)行。不難看出此處dispatch是個遞歸調用,多個中間件會形成一個棧結構。其中i的值總是比上一次傳進來的大,正常執(zhí)行index的值永遠小于i,但只要在同一個中間件中next執(zhí)行兩次以上,index的值就會等于i,同時會拋出錯誤。但如果不執(zhí)行next,中間件的處理也會終止。

整理下流程:

compose(this.middleware)(ctx)默認會執(zhí)行中間件數(shù)組中的第一個,也就是代碼中的dispatch(0),第一個中間件通過await next()返回的是第二個中間件的執(zhí)行。

然后第二個中間件中執(zhí)行await next(),然后返回第三個...以此類推

中間件全部處理結束以后,剩下的就是通過中間件中不斷傳遞的context來對請求作處理了。

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

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85225.html

相關文章

  • Express 實戰(zhàn)(一):概覽

    摘要:一個標準性的事件就是年的橫空出世。引擎快速處理能力和異步編程風格,讓開發(fā)者從多線程中解脫了出來。其次,通過異步編程范式將其高并發(fā)的能力發(fā)揮的淋漓盡致。它也僅僅是一個處理請求并作出響應的函數(shù),并無任何特殊之處。 showImg(https://segmentfault.com/img/remote/1460000010819116); 在正式學習 Express 內容之前,我們有必要從大...

    zhaochunqi 評論0 收藏0
  • 玩轉Koa -- 核心原理分析

    摘要:三中間件實現(xiàn)原理首先需要明確是中間件并不是中的概念,它只是和框架衍生的概念。中間件的執(zhí)行流程主要由與函數(shù)決定依次取出中間件終止條件路由匹配規(guī)則函數(shù)中使用閉包函數(shù)來檢測是否與當前路由相匹配,匹配則執(zhí)行該上的中間件函數(shù),否則繼續(xù)檢查下一個。 Koa作為下一代Web開發(fā)框架,不僅讓我們體驗到了async/await語法帶來同步方式書寫異步代碼的酸爽,而且本身簡潔的特點,更加利于開發(fā)者結合業(yè)務...

    jsbintask 評論0 收藏0
  • Express 實戰(zhàn)(四):中間件

    摘要:調用函數(shù)執(zhí)行下一個中間件函數(shù)。然后,該中間件調用函數(shù)檢查文件是否存在。為了代碼更加清晰,你也可以將代碼改寫為另外,這里在調用函數(shù)是使用的是作為輸出選項。事實上,中間件有兩種類型。 原生 Node 的單一請求處理函數(shù),隨著功能的擴張勢必會變的越來越難以維護。而 Express 框架則可以通過中間件的方式按照模塊和功能對處理函數(shù)進行切割處理。這樣拆分后的模塊不僅邏輯清晰,更重要的是對后期維...

    mochixuan 評論0 收藏0
  • 中間件執(zhí)行模塊koa-Compose源碼分析

    摘要:原文博客地址,歡迎學習交流點擊預覽讀了下的源碼,寫的相當?shù)木啠龅教幚碇虚g件執(zhí)行的模塊決定學習一下這個模塊的源碼。當在下游沒有更多的中間件執(zhí)行后,堆棧將展開并且每個中間件恢復執(zhí)行其上游行為。 原文博客地址,歡迎學習交流:點擊預覽 讀了下Koa的源碼,寫的相當?shù)木啠龅教幚碇虚g件執(zhí)行的模塊koa-Compose,決定學習一下這個模塊的源碼。 閱讀本文可以學到: Koa中間件的加載...

    imtianx 評論0 收藏0
  • Express 搭建服務器

    摘要:指定需要處理的路由回調函數(shù),即請求此路由的處理函數(shù),它可以接收兩個參數(shù)三個參數(shù),四個參數(shù)。如果匹配到自定義的路由,立即執(zhí)行回調函數(shù),如果處理函數(shù)中沒有則不再往下執(zhí)行,如果執(zhí)行了會繼續(xù)向下匹配。 簡介 Node.js? is a JavaScript runtime built on Chromes V8 JavaScript engine. Node.js uses an event-...

    CrazyCodes 評論0 收藏0

發(fā)表評論

0條評論

zhangyucha0

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<