Мы знаем, что промежуточное ПО Koa выполняется методом каскадного кода (Cascading). Подобно тому, как скрепки, вы можете обратиться к следующему изображению:
В сегодняшней статье будет проанализировано, как промежуточное ПО Koa реализует каскадное выполнение.
В koa для применения промежуточного программного обеспечения мы используемapp.use()
:
app
.use(logger())
.use(bodyParser())
.use(helmet())
Первый взглядuse()
Что это такое, его исходный код выглядит следующим образом:
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;
}
Цель этой функции - вызватьuse(fn)
Параметры в методе (будь то обычные функции или промежуточное ПО) добавляются вthis.middlware
в этом массиве.
существуетKoa2
в, даGenerator
Промежуточное ПО синтаксиса совместимо, используйтеisGeneratorFunction(fn)
этот метод, чтобы определить, является лиGenerator
синтаксис и черезconvert(fn)
Этот метод преобразуется вasync/await
грамматика. Затем добавьте все промежуточное ПО вthis.middleware
Наконец, поcallback()
Этот метод выполняется. Исходный код callback() выглядит следующим образом:
/**
* Return a request handler callback
* for node's native http server.
*
* @return {Function}
* @api public
*/
callback() {
const fn = compose(this.middleware);
if (!this.listeners('error').length) this.on('error', this.onerror);
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
В исходном коде черезcompose()
Этот метод может преобразовывать и каскадировать массив промежуточного программного обеспечения, которое мы передали, и, наконец,callback()
вернутьthis.handleRequest()
результат выполнения. Нам все равно, какой контент будет возвращен, давайте сначала посмотримcompose()
Что делает этот метод, чтобы позволить входящему промежуточному ПО быть каскадным и возвращатьPromise
.
compose()
Это библиотека для koa2 для реализации каскадных вызовов промежуточного программного обеспечения, называемаяkoa-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) {
// 记录上一次执行中间件的位置 #
let index = -1
return dispatch(0)
function dispatch (i) {
// 理论上 i 会大于 index,因为每次执行一次都会把 i递增,
// 如果相等或者小于,则说明next()执行了多次
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)
}
}
}
}
можно увидетьcompose()
Возвращает результат анонимной функции, которая выполняет сама себяdispatch()
Эта функция и передала 0 в качестве параметра.
приди и посмотриdispatch(i)
Что делает эта функция?i
В качестве параметра этой функции он используется для получения промежуточного программного обеспечения текущего индекса. надdispatch(0)
0 передается, чтобы получитьmiddleware[0]
промежуточное ПО.
Сначала проявите суждениеi<==index
,еслиtrue
Если да, то пояснитеnext()
Метод вызывается несколько раз. Почему можно так судить? Мы ответим на этот вопрос после того, как объясним всю логику.
Далее, текущийi
назначить наindex
, запишите индекс выполняющегося в данный момент ПО промежуточного слоя иfn
Сделайте задание, чтобы получить промежуточное ПО.
index = i;
let fn = middleware[i]
После получения промежуточного программного обеспечения, как его использовать?
try {
return Promise.resolve(fn(context, function next () {
return dispatch(i + 1)
}))
} catch (err) {
return Promise.reject(err)
}
Приведенный выше код выполняет промежуточное ПОfn(context, next)
, и прошелcontext
а такжеnext
функция с двумя параметрами.context
то естьkoa
объект контекста вcontext
. Что касаетсяnext
функция возвращаетdispatch(i+1)
результат выполнения. Стоит упомянуть, чтоi+1
Этот параметр, передача этого параметра эквивалентна выполнению следующего промежуточного программного обеспечения, тем самым формируя рекурсивный вызов.
Вот почему нам нужно выполнять вручную, когда мы сами пишем промежуточное ПО.
await next()
только выполненоnext
для правильного выполнения следующего промежуточного программного обеспечения.
Таким образом, каждое промежуточное ПО может быть выполнено только один раз.next
, если выполняется несколько раз в промежуточном программном обеспеченииnext
, будет проблема. Возвращаясь к вопросу, упомянутому ранее, почему вы сказали пройтиi<=index
могу судитьnext
Сколько раз?
потому что обычноindex
должно быть меньше или равноi
. При многократном вызове в промежуточном программном обеспеченииnext
, что приведет к многократному выполнениюdispatch(i+1)
. С точки зрения кода каждое промежуточное ПО имеет свою собственную область закрытия, и одно и то же промежуточное ПО имеет свою собственную область закрытия.i
постоянна, иindex
выходит за рамки замыкания.
Когда первое промежуточное ПО, т.е.dispatch(0)
изnext()
При вызове он должен выполняться в это времяdispatch(1)
, когда выполняется следующее суждение,
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
В настоящее времяindex
равно 0, иi
Значение равно 1, что не удовлетворяетi<=index
Это условие, продолжайте выполнять следующиеindex=i
задание, когдаindex
значение 1. Но если первое промежуточное ПО выполняется еще разnext()
, то он снова выполнитсяdispatch(2)
. Как упоминалось выше, в одном промежуточном программном обеспеченииi
Значение не изменилось, поэтому в настоящее времяi
Значение по-прежнему равно 1, поэтому это приводит кi <= index
Случай.
Может у кого есть вопросы? теперь, когдаasync
сам возвращаетсяPromise
, почему вы все еще используетеPromise.resolve()
Заверните. Это должно быть совместимо с обычными функциями, чтобы обычные функции также можно было использовать в обычном режиме.
Вернемся к механизму выполнения промежуточного программного обеспечения, чтобы посмотреть, что происходит.
мы знаемasync
Механизм исполнения таков: только когда всеawait
Async может быть возвращен только после завершения выполненияPromise
. Итак, когда мы используемasync
При написании промежуточного ПО с синтаксисом , поток выполнения выглядит примерно следующим образом:
- Сначала выполните первое промежуточное программное обеспечение (потому что
compose
будет выполняться по умолчаниюdispatch(0)
), промежуточное ПО возвращаетPromise
, а затем поKoa
Отслеживайте, выполняйте соответствующую логику (успех или неудача) - При выполнении логики первого мидлвара сталкиваемся
await next()
, он будет продолжать выполнятьсяdispatch(i+1)
, то есть выполнитьdispatch(1)
, который вручную инициирует выполнение второго промежуточного ПО. В это время первое промежуточное ПОawait next()
Код позади будет ожидать, ожидаяawait next()
вернутьPromise
, продолжит выполнение первого промежуточного ПОawait next()
код позади. - Точно так же при выполнении второго промежуточного программного обеспечения столкнитесь
await next()
Когда третье промежуточное ПО выполняется вручную,await next()
Код позади все еще находится на рассмотрении, ждетawait
следующее промежуточное ПОPromise.resolve
. Только после получения третьего промежуточного ПОresolve
После этого код позади будет выполнен, а затем вернется второй промежуточныйPromise
, по первому промежуточному ПОawait
Capture, затем будет выполнен последующий код первого промежуточного ПО, а затем возвратPromise
- По аналогии, если есть несколько промежуточных программ, они будут выполняться непрерывно в соответствии с приведенной выше логикой, первая промежуточная программа будет выполняться первой, а затем будет выполняться первая промежуточная программа.
await next()
Вне ожидания, продолжайте выполнять второе промежуточное ПО, продолжайтеawait next()
Вне ожидания, продолжайте выполнять третье промежуточное ПО, пока не будет выполнено последнее промежуточное ПО, затем вернитесь в Promise, затем предпоследнее промежуточное ПО выполнит последующий код и вернется в Promise, затем предпоследнее промежуточное ПО и затем продолжается с Этот метод выполняется до тех пор, пока первое промежуточное ПО не будет выполнено и не вернетPromise
, чтобы понять порядок выполнения картинки в начале статьи.
После приведенного выше анализа, если вы хотите написать промежуточное ПО koa2, то базовый формат должен быть следующим:
async function koaMiddleware(ctx, next){
try{
// do something
await next()
// do something
}
.catch(err){
// handle err
}
}
В последнее время я использую koa2 + React для ведения блога.Заинтересованные студенты могут перейти по адресу GitHub для просмотра:koa-blog-api