Familiar with koa-compose middleware source code

Goethe once said: reading a good book, is talking with noble people. The same can be said: read the source code, but also a way to learn and communicate with the author.

reference

Juejin. Cn/post / 700537…

koa-compose

Middleware refers to independent plug-ins that link the entire Koa application and share resources, “link” for “next” and “shared resources for context”.

There is a typical middleware GIF in the KOA documentation

compose

The middleware process is handled primarily with the compose function

Compose executes the logic:

  • Accepts an argument, the checksum argument is an array, and each entry in the checksum array is a function.
  • Returns a function that takes two arguments, respectivelycontextandnext, this function finally returnsPromise.
/**
 * Compose `middleware` returning
 * a fully valid middleware comprised
 * of all those which are passed.
 *
 * @param {Array} middleware
 * @return {Function}
 * @api public* /
function compose (middleware) {
  // Check that each item in the array is a function
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array! ')
  for (const fn of middleware) {
    if (typeoffn ! = ='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){
      // omit, as described below}}}Copy the code

dispatch

function dispatch (i) {
  Fn1, fn2, fn3...
  let fn = middleware[i]
 Compose (stack)({}) executes the first fn in the middleware array, next(), dispatch.bind(null, I + 1), next FN in the middleware array, etc
   return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))}Copy the code
  • complete
# 3 Details1.The next function is called multiple times in fn, where the index closure holds the count2.Last fn has no next, returnsPromise.resolve()
3.Error capture is wrapped by the previous FNfunction dispatch (i) {
  // Multiple calls to a function fail
  // await next() where index is closure data and will be updated every time the dispatch is dropped. If fn executes next twice, the update will clash
  // await next()
  if (i <= index) return Promise.reject(new Error('next() called multiple times'))
  index = i
  Fn1, fn2, fn3...
  let fn = middleware[i]
  // next = undefined
  if (i === middleware.length) fn = next
  // Return promise.resolve ()
  if(! fn)return Promise.resolve()
    // When an error occurs, the next function of the last fn is not executed. The catch function of the last fn is executed
  try {
    return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))}catch (err) {
    return Promise.reject(err)
  }
}
Copy the code

reporter

1. In the bind function, if this is not required, it is written as null

2. Closures can be used to count and check repeated execution

Function compose(){let index = -1 dispatch(0) dispatch(I){// dispatch(0) 0 "0" 1 "0" 1 "1" error if(i<=index) throw .. index = i return fn(dispatch(i+1)) } } fn(next){ next() next() }Copy the code

3. Sichuan elder brother yyds