koaソースを読み、Koaミドルウェアがどのように動作しているか(DAY 1)
KoaのミドルウェアはExpressとは異なり,Koaはタマネギモデルを用いた.不思議なKoaフレームワークには4つのファイルしか含まれていません.今日はメインファイル-アプリケーションだけを見てみましょう.jsは、ミドルウェアがどのように動作するかというコアロジックを含んでいます.
プロジェクトのルートディレクトリにindexを追加します.jsファイルは、テスト用途として使用されます.
サーバの実行:
アクセスhttp://localhost:30001,2,3,4,5,6の出力が表示されます.これをタマネギモデル(ミドルウェア)と言います.
Koaのコアコードを見て、ミドルウェアの動作原理を理解しましょう.indexでjsファイルでは、ミドルウェアをこのように使用できます.
次にアプリケーションを見てみましょうjs、次のコードはミドルウェアに関係しています.コードにコメントを追加しました.
compose関数の詳細についてはkoa-composeパッケージを見てみましょう
すべてのミドルウェアはcompose関数に渡され、dispatch(0)が返され、dispatch関数はすぐに実行され、promiseが返されます.dispatch関数の内容を理解する前にpromiseの構文を理解しなければなりません.
通常、promiseはこのように使用されています.
Koaではpromiseはこのように使用されています.
compose関数ではpromiseが返されることを知っています
dispatchは、すべてのミドルウェアを巡回する再帰関数です.私たちのindexではjsファイルにはawait next()の前にコードを実行する3つのミドルウェアがあります
3つのミドルウェアの実行順序を見ることができます.実行 実行するときの最初のミドルウェアは、 に動作する.第2のミドルウェアは、 である.第2のミドルウェアは、 まで動作する.第3のミドルウェアは、 である.第3のミドルウェアは、 まで動作する. が実行する.は、第2のミドルウェアのうち を実行する.は、第1のミドルウェアのうち が実行する.
ミドルウェアにasync/awaitがある場合、符号化はより簡単になります.apiリクエストに対するタイムレコーダを書きたい場合は、非常に簡単なことです.
の準備を
git clone [email protected]:koajs/koa.git
npm install
プロジェクトのルートディレクトリにindexを追加します.jsファイルは、テスト用途として使用されます.
// index.js
// Include the entry file of koa
const Koa = require('./lib/application.js');
const app = new Koa();
const debug = require('debug')('koa');
app.use(async (ctx, next) => {
console.log(1);
await next();
console.log(6);
const rt = ctx.response.get('X-Response-Time');
console.log(`${ctx.method} ${ctx.url} - ${rt}`);
});
// time logger here
app.use(async (ctx, next) => {
console.log(2);
const start = Date.now();
await next();
console.log(5);
const ms = Date.now() - start;
ctx.set('X-Response-Time', `${ms}ms`);
});
app.use(async (ctx, next) => {
console.log(3);
ctx.body = 'Hello World';
await next();
console.log(4);
});
app.listen(3000);
サーバの実行:
node index.js
アクセスhttp://localhost:30001,2,3,4,5,6の出力が表示されます.これをタマネギモデル(ミドルウェア)と言います.
タマネギの模型はどのように働きます
Koaのコアコードを見て、ミドルウェアの動作原理を理解しましょう.indexでjsファイルでは、ミドルウェアをこのように使用できます.
const app = new Koa();
app.use(// middleware);
app.use(// middleware);
app.listen(3000);
次にアプリケーションを見てみましょうjs、次のコードはミドルウェアに関係しています.コードにコメントを追加しました.
const compose = require('koa-compose');
module.exports = class Application extends Emitter {
constructor() {
super();
this.proxy = false;
// Step 0: init a middleware list
this.middleware = [];
}
use(fn) {
// Step 1: adding the middleware to the list
this.middleware.push(fn);
return this;
}
listen(...args) {
debug('listen');
// Step 2: using this.callback() to compose all middleware
const server = http.createServer(this.callback());
return server.listen(...args);
}
callback() {
// Step 3: This is the most important part - compose, it group all
// middleware to one big function and return a promise, we will talk more
// about this function
const fn = compose(this.middleware);
if (!this.listenerCount('error')) this.on('error', this.onerror);
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404;
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
onFinished(res, onerror);
// Step 4: Resolve the promise
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}
}
Component関数について
compose関数の詳細についてはkoa-composeパッケージを見てみましょう
module.exports = compose
function compose (middleware) {
// skipped type checking code here
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)
}
}
}
}
すべてのミドルウェアはcompose関数に渡され、dispatch(0)が返され、dispatch関数はすぐに実行され、promiseが返されます.dispatch関数の内容を理解する前にpromiseの構文を理解しなければなりません.
Promiseについて
通常、promiseはこのように使用されています.
const promise = new Promise(function(resolve, reject) {
if (success){
resolve(value);
} else {
reject(error);
}
});
Koaではpromiseはこのように使用されています.
let testPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('test success');
}, 1000);
});
Promise.resolve(testPromise).then(function (value) {
console.log(value); // "test success"
});
compose関数ではpromiseが返されることを知っています
Koa-composeミドルウェアに戻る
module.exports = compose
function compose (middleware) {
// skipped type checking code here
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)
}
}
}
}
dispatchは、すべてのミドルウェアを巡回する再帰関数です.私たちのindexではjsファイルにはawait next()の前にコードを実行する3つのミドルウェアがあります
app.use(async (ctx, next) => {
console.log(2);
const start = Date.now();
await next(); //
3つのミドルウェアの実行順序を見ることができます.
dispatch(0), Promise.resolve(fn(context, dispatch.bind(null,0+1)))
がawait next()
のnext() = dispatch.bind(null, 0+1)
await next()
next() = dispatch.bind(null, 1+1)
await next()
next() = dispatch.bind(null, 2+1)
、4番目のミドルウェアがない場合、直ちにif(!fn) return Promise.resolve()
に戻り、3番目のミドルウェアのうちawait next()
がresolvedされ、3番目のミドルウェアの残りのコードawait next()
がresolveする、第2のミドルウェアの残りのコードawait next()
がresolveされ、第1のミドルウェアの残りのコードなぜタマネギの模型を使うのですか?
ミドルウェアにasync/awaitがある場合、符号化はより簡単になります.apiリクエストに対するタイムレコーダを書きたい場合は、非常に簡単なことです.
app.use(async (ctx, next) => {
const start = Date.now();
await next(); // your API logic
const ms = Date.now() - start;
console.log('API response time:' + ms);
});