日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

50行代码串行Promise,koa洋葱模型原来这么有趣?

發(fā)布時(shí)間:2023/12/9 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 50行代码串行Promise,koa洋葱模型原来这么有趣? 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1. 前言

大家好,我是若川,最近組織了源碼共讀活動(dòng)《1個(gè)月,200+人,一起讀了4周源碼》,感興趣的可以加我微信 ruochuan12 參與,長(zhǎng)期交流學(xué)習(xí)。

之前寫的《學(xué)習(xí)源碼整體架構(gòu)系列》 包含jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex4十余篇源碼文章。其中最新的兩篇是:

Vue 3.2 發(fā)布了,那尤雨溪是怎么發(fā)布 Vue.js 的?

初學(xué)者也能看懂的 Vue3 源碼中那些實(shí)用的基礎(chǔ)工具函數(shù)

寫相對(duì)很難的源碼,耗費(fèi)了自己的時(shí)間和精力,也沒(méi)收獲多少閱讀點(diǎn)贊,其實(shí)是一件挺受打擊的事情。從閱讀量和讀者受益方面來(lái)看,不能促進(jìn)作者持續(xù)輸出文章。

所以轉(zhuǎn)變思路,寫一些相對(duì)通俗易懂的文章。其實(shí)源碼也不是想象的那么難,至少有很多看得懂

之前寫過(guò) koa 源碼文章學(xué)習(xí) koa 源碼的整體架構(gòu),淺析koa洋蔥模型原理和co原理比較長(zhǎng),讀者朋友大概率看不完,所以本文從koa-compose50行源碼講述。

本文涉及到的 koa-compose 倉(cāng)庫(kù)[1] 文件,整個(gè)index.js文件代碼行數(shù)雖然不到 50 行,而且測(cè)試用例test/test.js文件 300 余行,但非常值得我們學(xué)習(xí)。

歌德曾說(shuō):讀一本好書,就是在和高尚的人談話。同理可得:讀源碼,也算是和作者的一種學(xué)習(xí)交流的方式。

閱讀本文,你將學(xué)到:

1.?熟悉?koa-compose?中間件源碼、可以應(yīng)對(duì)面試官相關(guān)問(wèn)題 2.?學(xué)會(huì)使用測(cè)試用例調(diào)試源碼 3.?學(xué)會(huì)?jest?部分用法

2. 環(huán)境準(zhǔn)備

2.1 克隆 koa-compose 項(xiàng)目

本文倉(cāng)庫(kù)地址 koa-compose-analysis[2],求個(gè)star~

#?可以直接克隆我的倉(cāng)庫(kù),我的倉(cāng)庫(kù)保留的?compose?倉(cāng)庫(kù)的?git?記錄 git?clone?https://github.com/lxchuan12/koa-compose-analysis.git cd?koa-compose/compose npm?i

順帶說(shuō)下:我是怎么保留 compose 倉(cāng)庫(kù)的 git 記錄的。

#?在?github?上新建一個(gè)倉(cāng)庫(kù)?`koa-compose-analysis`?克隆下來(lái) git?clone?https://github.com/lxchuan12/koa-compose-analysis.git cd?koa-compose-analysis git?subtree?add?--prefix=compose?https://github.com/koajs/compose.git?main #?這樣就把 compose 文件夾克隆到自己的 git 倉(cāng)庫(kù)了。且保留的 git 記錄

關(guān)于更多 git subtree,可以看這篇文章用 Git Subtree 在多個(gè) Git 項(xiàng)目間雙向同步子項(xiàng)目,附簡(jiǎn)明使用手冊(cè)[3]

接著我們來(lái)看怎么根據(jù)開(kāi)源項(xiàng)目中提供的測(cè)試用例調(diào)試源碼。

2.2 根據(jù)測(cè)試用例調(diào)試 compose 源碼

用VSCode(我的版本是 1.60 )打開(kāi)項(xiàng)目,找到 compose/package.json,找到 scripts 和 test 命令。

//?compose/package.json {"name":?"koa-compose",//?debug?(調(diào)試)"scripts":?{"eslint":?"standard?--fix?.","test":?"jest"}, }

在scripts上方應(yīng)該會(huì)有debug或者調(diào)試字樣。點(diǎn)擊debug(調(diào)試),選擇 test。

VSCode 調(diào)試

接著會(huì)執(zhí)行測(cè)試用例test/test.js文件。終端輸出如下圖所示。

koa-compose 測(cè)試用例輸出結(jié)果

接著我們調(diào)試 compose/test/test.js 文件。我們可以在 45行 打上斷點(diǎn),重新點(diǎn)擊 package.json => srcipts => test 進(jìn)入調(diào)試模式。如下圖所示。

koa-compose 調(diào)試

接著按上方的按鈕,繼續(xù)調(diào)試。在compose/index.js文件中關(guān)鍵的地方打上斷點(diǎn),調(diào)試學(xué)習(xí)源碼事半功倍。

更多 nodejs 調(diào)試相關(guān) 可以查看官方文檔[4]

順便提一下幾個(gè)調(diào)試相關(guān)按鈕。

  • 繼續(xù)(F5)

  • 單步跳過(guò)(F10)

  • 單步調(diào)試(F11)

  • 單步跳出(Shift + F11)

  • 重啟(Ctrl + Shift + F5)

  • 斷開(kāi)鏈接(Shift + F5)

  • 接下來(lái),我們跟著測(cè)試用例學(xué)源碼。

    3. 跟著測(cè)試用例學(xué)源碼

    分享一個(gè)測(cè)試用例小技巧:我們可以在測(cè)試用例處加上only修飾。

    //?例如 it.only('should?work',?async?()?=>?{})

    這樣我們就可以只執(zhí)行當(dāng)前的測(cè)試用例,不關(guān)心其他的,不會(huì)干擾調(diào)試。

    3.1 正常流程

    打開(kāi) compose/test/test.js 文件,看第一個(gè)測(cè)試用例。

    //?compose/test/test.js 'use?strict'/*?eslint-env?jest?*/const?compose?=?require('..') const?assert?=?require('assert')function?wait?(ms)?{return?new?Promise((resolve)?=>?setTimeout(resolve,?ms?||?1)) } //?分組 describe('Koa?Compose',?function?()?{it.only('should?work',?async?()?=>?{const?arr?=?[]const?stack?=?[]stack.push(async?(context,?next)?=>?{arr.push(1)await?wait(1)await?next()await?wait(1)arr.push(6)})stack.push(async?(context,?next)?=>?{arr.push(2)await?wait(1)await?next()await?wait(1)arr.push(5)})stack.push(async?(context,?next)?=>?{arr.push(3)await?wait(1)await?next()await?wait(1)arr.push(4)})await?compose(stack)({})//?最后輸出數(shù)組是?[1,2,3,4,5,6]expect(arr).toEqual(expect.arrayContaining([1,?2,?3,?4,?5,?6]))}) }

    大概看完這段測(cè)試用例,context是什么,next又是什么。

    在`koa`的文檔[5]上有個(gè)非常代表性的中間件 gif 圖。

    中間件 gif 圖

    而compose函數(shù)作用就是把添加進(jìn)中間件數(shù)組的函數(shù)按照上面 gif 圖的順序執(zhí)行。

    3.1.1 compose 函數(shù)

    簡(jiǎn)單來(lái)說(shuō),compose 函數(shù)主要做了兩件事情。

  • 接收一個(gè)參數(shù),校驗(yàn)參數(shù)是數(shù)組,且校驗(yàn)數(shù)組中的每一項(xiàng)是函數(shù)。

    • 返回一個(gè)函數(shù),這個(gè)函數(shù)接收兩個(gè)參數(shù),分別是context和next,這個(gè)函數(shù)最后返回Promise。

    • /***?Compose?`middleware`?returning*?a?fully?valid?middleware?comprised*?of?all?those?which?are?passed.**?@param?{Array}?middleware*?@return?{Function}*?@api?public*/ function?compose?(middleware)?{//?校驗(yàn)傳入的參數(shù)是數(shù)組,校驗(yàn)數(shù)組中每一項(xiàng)是函數(shù)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?=?-1return?dispatch(0)function?dispatch(i){//?省略,下文講述}} }

      接著我們來(lái)看 dispatch 函數(shù)。

      3.1.2 dispatch 函數(shù)

      function?dispatch?(i)?{//?一個(gè)函數(shù)中多次調(diào)用報(bào)錯(cuò)//?await?next()//?await?next()if?(i?<=?index)?return?Promise.reject(new?Error('next()?called?multiple?times'))index?=?i//?取出數(shù)組里的?fn1,?fn2,?fn3...let?fn?=?middleware[i]//?最后?相等,next?為?undefinedif?(i?===?middleware.length)?fn?=?next//?直接返回?Promise.resolve()if?(!fn)?return?Promise.resolve()try?{return?Promise.resolve(fn(context,?dispatch.bind(null,?i?+?1)))}?catch?(err)?{return?Promise.reject(err)} }

      值得一提的是:bind函數(shù)是返回一個(gè)新的函數(shù)。第一個(gè)參數(shù)是函數(shù)里的this指向(如果函數(shù)不需要使用this,一般會(huì)寫成null)。這句fn(context, dispatch.bind(null, i + 1),i + 1 是為了 let fn = middleware[i] 取middleware中的下一個(gè)函數(shù)。也就是 next 是下一個(gè)中間件里的函數(shù)。也就能解釋上文中的 gif圖函數(shù)執(zhí)行順序。測(cè)試用例中數(shù)組的最終順序是[1,2,3,4,5,6]。

      3.1.3 簡(jiǎn)化 compose 便于理解

      自己動(dòng)手調(diào)試之后,你會(huì)發(fā)現(xiàn) compose 執(zhí)行后就是類似這樣的結(jié)構(gòu)(省略 try catch 判斷)。

      //?這樣就可能更好理解了。 //?simpleKoaCompose const?[fn1,?fn2,?fn3]?=?stack; const?fnMiddleware?=?function(context){return?Promise.resolve(fn1(context,?function?next(){return?Promise.resolve(fn2(context,?function?next(){return?Promise.resolve(fn3(context,?function?next(){return?Promise.resolve();}))}))})); };

      也就是說(shuō)koa-compose返回的是一個(gè)Promise,從中間件(傳入的數(shù)組)中取出第一個(gè)函數(shù),傳入context和第一個(gè)next函數(shù)來(lái)執(zhí)行。
      第一個(gè)next函數(shù)里也是返回的是一個(gè)Promise,從中間件(傳入的數(shù)組)中取出第二個(gè)函數(shù),傳入context和第二個(gè)next函數(shù)來(lái)執(zhí)行。
      第二個(gè)next函數(shù)里也是返回的是一個(gè)Promise,從中間件(傳入的數(shù)組)中取出第三個(gè)函數(shù),傳入context和第三個(gè)next函數(shù)來(lái)執(zhí)行。
      第三個(gè)...
      以此類推。最后一個(gè)中間件中有調(diào)用next函數(shù),則返回Promise.resolve。如果沒(méi)有,則不執(zhí)行next函數(shù)。這樣就把所有中間件串聯(lián)起來(lái)了。這也就是我們常說(shuō)的洋蔥模型。

      洋蔥模型圖如下圖所示:

      不得不說(shuō)非常驚艷,“玩還是大神會(huì)玩”

      3.2 錯(cuò)誤捕獲

      it('should?catch?downstream?errors',?async?()?=>?{const?arr?=?[]const?stack?=?[]stack.push(async?(ctx,?next)?=>?{arr.push(1)try?{arr.push(6)await?next()arr.push(7)}?catch?(err)?{arr.push(2)}arr.push(3)})stack.push(async?(ctx,?next)?=>?{arr.push(4)throw?new?Error()})await?compose(stack)({})//?輸出順序?是?[?1,?6,?4,?2,?3?]expect(arr).toEqual([1,?6,?4,?2,?3]) })

      相信理解了第一個(gè)測(cè)試用例和 compose 函數(shù),也是比較好理解這個(gè)測(cè)試用例了。這一部分其實(shí)就是對(duì)應(yīng)的代碼在這里。

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

      3.3 next 函數(shù)不能調(diào)用多次

      it('should?throw?if?next()?is?called?multiple?times',?()?=>?{return?compose([async?(ctx,?next)?=>?{await?next()await?next()}])({}).then(()?=>?{throw?new?Error('boom')},?(err)?=>?{assert(/multiple?times/.test(err.message))}) })

      這一塊對(duì)應(yīng)的則是:

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

      調(diào)用兩次后 i 和 index 都為 1,所以會(huì)報(bào)錯(cuò)。

      compose/test/test.js文件中總共 300余行,還有很多測(cè)試用例可以按照文中方法自行調(diào)試。

      4. 總結(jié)

      雖然koa-compose源碼 50行 不到,但如果是第一次看源碼調(diào)試源碼,還是會(huì)有難度的。其中混雜著高階函數(shù)、閉包、Promise、bind等基礎(chǔ)知識(shí)。

      通過(guò)本文,我們熟悉了 koa-compose 中間件常說(shuō)的洋蔥模型,學(xué)會(huì)了部分 `jest`[6] 用法,同時(shí)也學(xué)會(huì)了如何使用現(xiàn)成的測(cè)試用例去調(diào)試源碼。

      相信學(xué)會(huì)了通過(guò)測(cè)試用例調(diào)試源碼后,會(huì)覺(jué)得源碼也沒(méi)有想象中的那么難

      開(kāi)源項(xiàng)目,一般都會(huì)有很全面的測(cè)試用例。除了可以給我們學(xué)習(xí)源碼調(diào)試源碼帶來(lái)方便的同時(shí),也可以給我們帶來(lái)的啟發(fā):自己工作中的項(xiàng)目,也可以逐步引入測(cè)試工具,比如 jest。

      此外,讀開(kāi)源項(xiàng)目源碼是我們學(xué)習(xí)業(yè)界大牛設(shè)計(jì)思想和源碼實(shí)現(xiàn)等比較好的方式。

      看完本文,非常希望能自己動(dòng)手實(shí)踐調(diào)試源碼去學(xué)習(xí),容易吸收消化。另外,如果你有余力,可以繼續(xù)看我的 koa-compose 源碼文章:學(xué)習(xí) koa 源碼的整體架構(gòu),淺析koa洋蔥模型原理和co原理

      參考資料

      [1]

      koa-compose 倉(cāng)庫(kù): https://github.com/koajs/compose

      [2]

      本文倉(cāng)庫(kù)地址 koa-compose-analysis: https://github.com/lxchuan12/koa-compose-analysis.git

      [3]

      用 Git Subtree 在多個(gè) Git 項(xiàng)目間雙向同步子項(xiàng)目,附簡(jiǎn)明使用手冊(cè): https://segmentfault.com/a/1190000003969060

      [4]

      更多 nodejs 調(diào)試相關(guān) 可以查看官方文檔: https://code.visualstudio.com/docs/nodejs/nodejs-debugging

      [5]

      koa的文檔: https://github.com/koajs/koa/blob/master/docs/guide.md#writing-middleware

      [6]

      jest: https://github.com/facebook/jest

      最近組建了一個(gè)江西人的前端交流群,如果你是江西人可以加我微信?ruochuan12?私信 江西?拉你進(jìn)群。


      推薦閱讀

      1個(gè)月,200+人,一起讀了4周源碼
      我讀源碼的經(jīng)歷

      老姚淺談:怎么學(xué)JavaScript?

      我在阿里招前端,該怎么幫你(可進(jìn)面試群)

      ·················?若川簡(jiǎn)介?·················

      你好,我是若川,畢業(yè)于江西高校。現(xiàn)在是一名前端開(kāi)發(fā)“工程師”。寫有《學(xué)習(xí)源碼整體架構(gòu)系列》多篇,在知乎、掘金收獲超百萬(wàn)閱讀。
      從2014年起,每年都會(huì)寫一篇年度總結(jié),已經(jīng)寫了7篇,點(diǎn)擊查看年度總結(jié)。
      同時(shí),活躍在知乎@若川,掘金@若川。致力于分享前端開(kāi)發(fā)經(jīng)驗(yàn),愿景:幫助5年內(nèi)前端人走向前列。

      識(shí)別方二維碼加我微信、拉你進(jìn)源碼共讀

      今日話題

      略。歡迎分享、收藏、點(diǎn)贊、在看我的公眾號(hào)文章~

    總結(jié)

    以上是生活随笔為你收集整理的50行代码串行Promise,koa洋葱模型原来这么有趣?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。