Today in the process of learning KOA, I encountered an asynchronous situation that does not return the result, as follows

app.use((ctx, next) = > {
    new Promise(resolve= > {
        setTimeout((a)= > {
            ctx.body = 'hello'
            resolve()
        }, 1000)})})Copy the code

To return correctly, it is easy to check the previous middleware and make sure to await next() or return next().

// Middleware in front
app.use(async (ctx, next) => await next())
/ / or
app.use((ctx, next) = > return next())

/ / then
app.use((async ctx, next) = > {
    await new Promise(resolve= > {
        setTimeout((a)= > {
            ctx.body = 'hello'
            resolve()
        }, 1000)})// Return new Promise(...) That's ok
})
Copy the code

At first I was confused that even though there is no need to return a value and the value returned is null, so what difference does it make to await and not to add?

It wasn’t until I read the koA source code that the penny dropped.

Resolve (fn(CTX, dispatch.bind(null, I + 1))).then ctx.body will be returned when the middleware is finished, otherwise 404 will be returned.

  1. Adding Middleware
function task0 (ctx, next) {
    return next()
}
function async task1 (ctx, next) {
    await next()
}
function async task3 (ctx, next) {
    await new Promise(resolve= > {
        setTimeout((a)= > {
            ctx.body = 'hello'
        }, 1000)
    })  
}
app.use(task0);
app.use(task1);
app.use(task2);
Copy the code
  1. Middleware compose processing (Onion model)
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! ')}

  return function (context, next) {
    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)
      }
    }
  }
}
Copy the code

Simple code for when an HTTP request comes in:

// dispatch(0)
return Promise.resolve(task0(ctx, dispatch_1))

function task0 (ctx, dispatch_1) {
    return dispatch_1()
}
function dispatch_1 () {
    return Promise.resolve(task1(ctx, dispatch_2))
}
async function task1 (ctx, dispatch_2) {
    await dispatch_2()
}
function dispatch_2 () {
    return Promise.resolve(task2(ctx, dispatch_3))
}
async function task2 () {
    await new Promise(resolve= > {
        setTimeout((a)= > {
            ctx.body = 'hello'
            resolve()
        }, 1000)})}function dispatch_3 () {};
Copy the code

Another way to look at it, it should make more sense this way:

var ctx = {body: null}
function task0 () {
    return Promise.resolve(
        Promise.resolve(task1())
    )
};
async function task1 () {
    await Promise.resolve(
        await new Promise(resolve= > {
            setTimeout((a)= > {
                ctx.body = 'hello'
                resolve()
            }, 1000)}}));Promise.resolve(task0(ctx)).then((a)= > {
    console.log('res', ctx.body)
    handleResponse(ctx)
});

// res: 'hello'
Copy the code

If task0 neither returns next() (returns a new Promise object) nor await next(), it will immediately reach handleResponse(CTX) without ctx.body=’hello’ being executed.

So instead of express, next in KOA uses await next() or return next().