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

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

生活随笔

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

编程问答

koa2异常处理_读 koa2 源码后的一些思考与实践

發(fā)布時(shí)間:2023/11/29 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 koa2异常处理_读 koa2 源码后的一些思考与实践 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

koa2的特點(diǎn)優(yōu)勢(shì)

什么是 koa2

  • Nodejs官方api支持的都是callback形式的異步編程模型。問(wèn)題:callback嵌套問(wèn)題
  • koa2 是由 Express原班人馬打造的,是現(xiàn)在比較流行的基于Node.js平臺(tái)的web開(kāi)發(fā)框架,Koa 把 Express 中內(nèi)置的 router、view 等功能都移除了,使得框架本身更輕量,而且擴(kuò)展性很強(qiáng)。使用koa編寫(xiě)web應(yīng)用,可以免除重復(fù)繁瑣的回調(diào)函數(shù)。
  • koa2 的優(yōu)點(diǎn)

    優(yōu)點(diǎn)這個(gè)東西,我直接說(shuō)它多好,你可能又不開(kāi)心,但是我們可以對(duì)比哦!這里我只說(shuō)它對(duì)比原生的 Node.js開(kāi)啟 http 服務(wù) 帶來(lái)了哪些優(yōu)點(diǎn)!

    • 先看一下原生 Node.js 我開(kāi)啟一個(gè) http 服務(wù)
    const http = require('http');http.createServer((req,res)=>{ res.writeHead(200); res.end('hi koala');}).listen(3000);
    • 看一下使用 koa2 開(kāi)啟一個(gè)http 服務(wù)
    const Koa = require('koa') ;const app = new Koa();const {createReadStream} = require('fs');app.use(async (ctx,next)=>{ if(ctx.path === '/favicon.ico'){ ctx.body = createReadStream('./avicon.ico') }else{ await next(); }});app.use(ctx=>{ ctx.body = 'hi koala';})app.listen(3000);

    我在 koa2 中添加了一個(gè)判斷 /favicon.ico 的實(shí)現(xiàn) 通過(guò)以上兩段代碼,會(huì)發(fā)現(xiàn)下面幾個(gè)優(yōu)點(diǎn)

  • 傳統(tǒng)的 http 服務(wù)想使用模塊化不是很方便,我們不能在一個(gè)服務(wù)里面判斷所有的請(qǐng)求和一些內(nèi)容。而 koa2 對(duì)模塊化提供了更好的幫助
  • koa2 把 req,res 封裝到了 context 中,更簡(jiǎn)潔而且方便記憶
  • 中間件機(jī)制,采用洋蔥模型,洋蔥模型流程記住一點(diǎn)(洋蔥是先從皮到心,然后從心到皮),通過(guò)洋蔥模型把代碼流程化,讓流水線更加清楚,如果不使用中間件,在 createServer 一條線判斷所有邏輯確實(shí)不好。
  • 看不到的優(yōu)點(diǎn)也很多,error 錯(cuò)誤處理,res的封裝處理等。
  • 自己實(shí)現(xiàn)一個(gè)koa2

    在實(shí)現(xiàn)的過(guò)程中會(huì)我看看可以學(xué)到那些知識(shí)

    listen 函數(shù)簡(jiǎn)單封裝

    koa2 直接使用的時(shí)候,我們通過(guò) const app = new Koa();,koa 應(yīng)該是一個(gè)類,而且可以直接調(diào)用 listen 函數(shù),并且沒(méi)有暴漏出http 服務(wù)的創(chuàng)建,說(shuō)明在listen函數(shù)中可能創(chuàng)建了服務(wù)。到此簡(jiǎn)單代碼實(shí)現(xiàn)應(yīng)該是這樣的:

    class Kkb{ constructor(){ this.middlewares = []; } listen(...args){ http.createServer(async (req,res)=>{ // 給用戶返回信息 this.callback(req,res); res.writeHead(200); res.statusCode = 200; res.end('hello koala') }).listen(...args) }}module.exports = Kkb;

    實(shí)現(xiàn) context 的封裝

    實(shí)現(xiàn)了簡(jiǎn)單 listen 后,會(huì)發(fā)現(xiàn)回調(diào)函數(shù)返回的還是 req 和 res ,要是將二者封裝到 context 一次返回就更好了!我們繼續(xù)

    const ctx = this.createContext(req,res);

    看一下 createContext 的具體實(shí)現(xiàn)

    const request = require('./lib/request');const response = require('./lib/response');const context = require('./lib/context'); createContext(req,res){ // 創(chuàng)建一個(gè)新對(duì)象,繼承導(dǎo)入的context const ctx = Object.create(context); ctx.request = Object.create(request); ctx.response = Object.create(response); // 這里的兩等于判斷,讓使用者既可以直接使用ctx,也可以使用原生的內(nèi)容 ctx.req = ctx.request.req = req; ctx.res = ctx.response.res = res; return ctx; }

    context.js

    module.exports = { get url(){ return this.request.url; }, get body(){ return this.response.body; }, set body(val){ this.response.body = val; }}

    request.js

    module.exports = { get url(){ return this.req.url; }}

    這里在寫(xiě) context.js 時(shí)候,用到了set 與 get 函數(shù),get 語(yǔ)句作為函數(shù)綁定在對(duì)象的屬性上,當(dāng)訪問(wèn)該屬性時(shí)調(diào)用該函數(shù)。set 語(yǔ)法可以將一個(gè)函數(shù)綁定在當(dāng)前對(duì)象的指定屬性上,當(dāng)那個(gè)屬性被賦值時(shí),你所綁定的函數(shù)就會(huì)被調(diào)用。

    實(shí)現(xiàn)洋蔥模型

    compose 另一個(gè)應(yīng)用場(chǎng)景

    說(shuō)洋蔥模型之前先看一個(gè)函數(shù)式編程內(nèi)容:compose 函數(shù)前端用過(guò) redux 的同學(xué)肯定都很熟悉。redux 通過(guò)compose來(lái)處理 中間件 。原理是 借助數(shù)組的 reduce 對(duì)數(shù)組的參數(shù)進(jìn)行迭代

    // redux 中的 compose 函數(shù)export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args)))}

    洋蔥模型實(shí)現(xiàn)

    再看文章開(kāi)頭 koa2 創(chuàng)建 http 服務(wù)函數(shù),會(huì)發(fā)現(xiàn)多次調(diào)用 use 函數(shù),其實(shí)這就是洋蔥模型的應(yīng)用。

    洋蔥是由很多層組成的,你可以把每個(gè)中間件看作洋蔥里的一層,根據(jù)app.use的調(diào)用順序中間件由外層到里層組成了整個(gè)洋蔥,整個(gè)中間件執(zhí)行過(guò)程相當(dāng)于由外到內(nèi)再到外地穿透整個(gè)洋蔥

    引用一張著名的洋蔥模型圖:

    每次執(zhí)行 use 函數(shù),我們實(shí)際是往一個(gè)函數(shù)數(shù)組中添加了一個(gè)函數(shù),然后再次通過(guò)一個(gè) compose 函數(shù),處理添加進(jìn)來(lái)函數(shù)的執(zhí)行順序,也就是這個(gè) compose 函數(shù)實(shí)現(xiàn)了洋蔥模型機(jī)制。

    具體代碼實(shí)現(xiàn)如下:

    // 其中包含一個(gè)遞歸 compose(middlewares){ return async function(ctx){// 傳入上下文 return dispatch(0); function dispatch(i){ let fn = middlewares[i]; if(!fn){ return Promise.resolve(); } return Promise.resolve( fn(ctx,function next(){ return dispatch(i+1) }) ) } } }

    首先執(zhí)行一次 dispatch(0) 也就是默認(rèn)返回第一個(gè) app.use 傳入的函數(shù) 使用 Promise 函數(shù)封裝返回,其中第一個(gè)參數(shù)是我們常用的 ctx,

    第二個(gè)參數(shù)就是 next 參數(shù),next 每次執(zhí)行之后都會(huì)等于下一個(gè)中間件函數(shù),如果下一個(gè)中間件函數(shù)不為真則返回一個(gè)成功的 Promise。因此我們每次調(diào)用 next() 就是在執(zhí)行下一個(gè)中間件函數(shù)。

    來(lái)試試我們自己實(shí)現(xiàn)的koa2

    使用一下我們自己的 koa2 吧,用它做一道常考洋蔥模型面試題,我想文章如果懂了,輸出結(jié)果應(yīng)該不會(huì)錯(cuò)了,自己試一下!

    const KKB = require('./kkb');const app = new KKB();app.use(async (ctx,next)=>{ ctx.body = '1'; await next(); ctx.body += '3';})app.use(async (ctx,next)=>{ ctx.body += '4'; await delay(); await next(); ctx.body += '5';})app.use(async (ctx,next)=>{ ctx.body += '6'})async function delay(){ return new Promise((reslove,reject)=>{ setTimeout(()=>{ reslove(); },1000); })}app.listen(3000);

    解題思路:還是洋蔥思想,洋蔥是先從皮到心,然后從心到皮

    答案: 1 4 6 5 3

    補(bǔ)充與說(shuō)明

    本文目的主要是讓大家學(xué)到一個(gè)koa2的基本流程,簡(jiǎn)單實(shí)現(xiàn)koa2,再去讀源碼有一個(gè)清晰的思路。實(shí)際源碼中還有很多優(yōu)秀的值得我們學(xué)習(xí)的點(diǎn),接下來(lái)再列舉一個(gè)我覺(jué)得它很優(yōu)秀的點(diǎn)——錯(cuò)誤處理,大家可在原有基礎(chǔ)上繼續(xù)實(shí)現(xiàn),也可以去讀源碼繼續(xù)看!加油加油

    源碼中 koa 繼承自 Emiiter,為了處理可能在任意時(shí)間拋出的異常所以訂閱了 error 事件。error 處理有兩個(gè)層面,一個(gè)是 app 層面全局的(主要負(fù)責(zé) log),另一個(gè)是一次響應(yīng)過(guò)程中的 error 處理(主要決定響應(yīng)的結(jié)果),koa 有一個(gè)默認(rèn) app-level 的 onerror 事件,用來(lái)輸出錯(cuò)誤日志。

    // 在調(diào)用洋蔥模型函數(shù)后面,koa 會(huì)掛載一個(gè)默認(rèn)的錯(cuò)誤處理【運(yùn)行時(shí)確定異常處理】 if (!this.listenerCount("error")) this.on("error", this.onerror); onerror(err) { if (!(err instanceof Error)) throw new TypeError(util.format("non-error thrown: %j", err)); if (404 == err.status || err.expose) return; if (this.silent) return; const msg = err.stack || err.toString(); console.error(); console.error(msg.replace(/^/gm, " ")); console.error(); }

    通過(guò) Emiiter 實(shí)現(xiàn)了錯(cuò)誤打印,Emiiter 采用了發(fā)布訂閱的設(shè)計(jì)模式,如果有對(duì) Emiiter 有不太清楚的小伙伴可以看我這篇文章

    [源碼解讀]一文徹底搞懂Events模塊

    總結(jié)

    本文注重思想,精簡(jiǎn)版本,代碼與實(shí)現(xiàn)都很簡(jiǎn)單。封裝,遞歸,設(shè)計(jì)模式都說(shuō)了一丟丟,希望也能對(duì)你有一丟丟的提升和讓你去看一下koa2源碼的想法,下篇文章見(jiàn)。

    ▼ 原創(chuàng)系列推薦▼

    TypeScript真香系列——接口篇

    消息隊(duì)列助你成為高薪 Node.js 工程師

    深入理解Node.js 進(jìn)程與線程(8000長(zhǎng)文徹底搞懂)

    [源碼解讀]一文徹底搞懂Events模塊

    Node.js 高級(jí)進(jìn)階之 fs 文件模塊學(xué)習(xí)

    Node進(jìn)階-探究不在V8堆內(nèi)存中存儲(chǔ)的Buffer對(duì)象

    說(shuō)Node.js做后端開(kāi)發(fā),stream有必要了解下

    點(diǎn)在看,分享給身邊的開(kāi)發(fā)

    總結(jié)

    以上是生活随笔為你收集整理的koa2异常处理_读 koa2 源码后的一些思考与实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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