亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

洋蔥模型?koa-compose源碼解析

 更新時(shí)間:2022年12月22日 08:46:47   作者:田八  
這篇文章主要為大家介紹了洋蔥模型?koa-compose源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

洋蔥模型

koa-compose是一個(gè)非常簡(jiǎn)單的函數(shù),它接受一個(gè)中間件數(shù)組,返回一個(gè)函數(shù),這個(gè)函數(shù)就是一個(gè)洋蔥模型的核心。

源碼地址:github.com/koajs/compo…

網(wǎng)上一搜一大把圖,我就不貼圖了,代碼也不上,因?yàn)榈葧?huì)源碼就是,這里只是介紹一下概念。

洋蔥模型是一個(gè)非常簡(jiǎn)單的概念,它的核心是一個(gè)函數(shù),這個(gè)函數(shù)接受一個(gè)函數(shù)數(shù)組,返回一個(gè)函數(shù),這個(gè)函數(shù)就是洋蔥模型的核心。

這個(gè)返回的函數(shù)就是聚合了所有中間件的函數(shù),它的執(zhí)行順序是從外到內(nèi),從內(nèi)到外。

例如:

  • 傳入一個(gè)中間件數(shù)組,數(shù)組中有三個(gè)中間件,分別是ab、c
  • 返回的函數(shù)執(zhí)行時(shí),會(huì)先執(zhí)行a,然后執(zhí)行b,最后執(zhí)行c
  • 執(zhí)行完c后,會(huì)從內(nèi)到外依次執(zhí)行b、a。
  • 執(zhí)行完a后,返回執(zhí)行結(jié)果。

這樣說(shuō)的可能還是不太清楚,來(lái)看一下流程圖:

這里是兩步操作,第一步是傳入中間件數(shù)組,第二步是執(zhí)行返回的函數(shù),而我們今天要解析就是第一步。

源碼

源碼并不多,只有不到 50 行,我們來(lái)看一下:

'use strict'
/**
 * Expose compositor.
 */
module.exports = compose
/**
 * Compose `middleware` returning
 * a fully valid middleware comprised
 * of all those which are passed.
 *
 * @param {Array} middleware
 * @return {Function}
 * @api public
 */
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, dispatch.bind(null, i + 1)))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

雖然說(shuō)不到 50 行,其實(shí)注釋都快占了一半,直接看源碼,先看兩個(gè)部分,第一個(gè)導(dǎo)出,第二個(gè)是返回的函數(shù)。

module.exports = compose
function compose (middleware) {
  // ...
  return function (context, next) {
      return dispatch(0)
      function dispatch (i) {
        // ...
      }
  }
}

這里確實(shí)是第一次見(jiàn)這樣玩變量提升的,所以先給大家講一下變量提升的規(guī)則:

  • 變量提升是在函數(shù)執(zhí)行前,函數(shù)內(nèi)部的變量和函數(shù)聲明會(huì)被提升到函數(shù)頂部。
  • 變量提升只會(huì)提升變量聲明,不會(huì)提升賦值。
  • 函數(shù)提升會(huì)提升函數(shù)聲明和函數(shù)表達(dá)式。
  • 函數(shù)提升會(huì)把函數(shù)聲明提升到函數(shù)頂部,函數(shù)表達(dá)式會(huì)被提升到變量聲明的位置。

這里的module.exports = compose是變量提升,function compose是函數(shù)提升,所以compose函數(shù)會(huì)被提升到module.exports之前。

下面的return dispatch(0)是函數(shù)內(nèi)部的變量提升,dispatch函數(shù)會(huì)被提升到return之前。

雖然這樣可行,但是不建議這樣寫(xiě),因?yàn)檫@樣寫(xiě)會(huì)讓代碼變得難以閱讀,不多說(shuō)了,繼續(xù)吧:

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!')
    }
    // ...
}

最開(kāi)始就是洋蔥模型的要求判斷了,中間件必須是數(shù)組,數(shù)組里面的每一項(xiàng)必須是函數(shù)。

繼續(xù)看:

return function (context, next) {
    // last called middleware #
    let index = -1
    return dispatch(0)
    // ...
}

這個(gè)函數(shù)是返回的函數(shù),這個(gè)函數(shù)接收兩個(gè)參數(shù),contextnextcontext是上下文,next是下一個(gè)中間件,這里的nextcompose函數(shù)的第二個(gè)參數(shù),也就是app.callback()的第二個(gè)參數(shù)。

index注釋寫(xiě)的很清楚,是最后一個(gè)調(diào)用的中間件的索引,這里初始化為-1,因?yàn)閿?shù)組的索引是從0開(kāi)始的。

dispatch函數(shù)是用來(lái)執(zhí)行中間件的,這里傳入0,也就是從第一個(gè)中間件開(kāi)始執(zhí)行。

function dispatch (i) {
    if (i <= index) return Promise.reject(new Error('next() called multiple times'))
    index = i
}

可以看到,dispatch函數(shù)接收一個(gè)參數(shù),這個(gè)參數(shù)是中間件的索引,這里的i就是dispatch(0)傳入的0。

這里的判斷是為了防止next被調(diào)用多次,如果i小于等于index,就會(huì)拋出一個(gè)錯(cuò)誤,這里的index-1,所以這個(gè)判斷是不會(huì)執(zhí)行的。

后面就賦值了indexi,這樣就可以防止next被調(diào)用多次了,繼續(xù)看:

let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()

這里的fn是中間件,也是當(dāng)前要執(zhí)行的中間件,通過(guò)索引直接從最開(kāi)始初始化的middleware數(shù)組里面取出來(lái)。

如果是到了最后一個(gè)中間件,這里的next指的是下一個(gè)中間件,也就是app.callback()的第二個(gè)參數(shù)。

如果fn不存在,就返回一個(gè)成功的Promise,表示所有的中間件都執(zhí)行完了。

繼續(xù)看:

try {
    return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
} catch (err) {
    return Promise.reject(err)
}

這里就是執(zhí)行中間件的地方了,fn是剛才取到的中間件,直接執(zhí)行。

然后傳入contextdispatch.bind(null, i + 1),這里的dispatch.bind(null, i + 1)就是next,也就是下一個(gè)中間件。

這里就有點(diǎn)遞歸的感覺(jué)了,但是并沒(méi)有直接調(diào)用,而是通過(guò)外部手動(dòng)調(diào)用next來(lái)執(zhí)行下一個(gè)中間件。

這里的try...catch是為了捕獲中間件執(zhí)行過(guò)程中的錯(cuò)誤,如果有錯(cuò)誤,就返回一個(gè)失敗的Promise。

動(dòng)手

老規(guī)矩,還是用class來(lái)實(shí)現(xiàn)一下這個(gè)compose函數(shù)。

class Compose {
    constructor(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!')
        }
        this.index = -1
        this.middleware = middleware
        return (next) => {
            this.next = next
            return this.dispatch(0)
        }
    }
    dispatch(i) {
        if (i <= this.index) return Promise.reject(new Error('next() called multiple times'))
        this.index = i
        let fn = this.middleware[i]
        if (i === this.middleware.length) fn = this.next
        if (!fn) return Promise.resolve()
        try {
            return Promise.resolve(fn(this.dispatch.bind(this, i + 1)))
        } catch (err) {
            return Promise.reject(err)
        }
    }
}
var middleware = [
    (next) => {
        console.log(1)
        next()
        console.log(2)
    },
    (next) => {
        console.log(3)
        next()
        console.log(4)
    },
    (next) => {
        console.log(5)
        next()
        console.log(6)
    }
]
var compose = new Compose(middleware)
compose()
var middleware = [
    (next) => {
        return next().then((res) => {
            return res + '1'
        })
    },
    (next) => {
        return next().then((res) => {
            return res + '2'
        })
    },
    (next) => {
        return next().then((res) => {
            return res + '3'
        })
    }
]
var compose = new Compose(middleware)
compose(() => {
    return Promise.resolve('0')
}).then((res) => {
    console.log(res)
})

這次不放執(zhí)行結(jié)果的截圖了,可以直接瀏覽器控制臺(tái)中自行執(zhí)行。

總結(jié)

koa-compose的實(shí)現(xiàn)原理就是通過(guò)遞歸來(lái)實(shí)現(xiàn)的,每次執(zhí)行中間件的時(shí)候,都會(huì)返回一個(gè)成功的Promise

其實(shí)這里不使用Promise也是可以的,但是使用Promise可以有效的處理異步和錯(cuò)誤。

而且從上面手動(dòng)實(shí)現(xiàn)的代碼案例中也可以看到,使用Promise可以有更多的靈活性,寫(xiě)法也是多元化。

以上就是洋蔥模型 koa-compose源碼解析的詳細(xì)內(nèi)容,更多關(guān)于洋蔥模型 koa-compose的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論