koa异步处理问题的思考

1,735 阅读2分钟

今日在学习koa的过程中遇到了异步不返回结果的情况, 如下

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

想正确返回也很简单,检查前面的中间件,保证是await next()或者是return next()

// 前面的中间件
app.use(async (ctx, next) => await next())
// 或者
app.use((ctx, next) => return next())

// 然后
app.use((async ctx, next) => {
    await new Promise(resolve => {
        setTimeout(() => {
            ctx.body = 'hello'
            resolve()
        }, 1000)
    })
    // 或者在这里 return new Promise(...) 也是可以的
})

一开始我困惑的是即然这里都不需要返回值,返回的也是空值,那么await加与不加又有什么差别呢?

直到我了解了koa的源码后才恍然大悟。

Promise.resolve(fn(ctx, dispatch.bind(null, i + 1))),当中间件走完,.then中的ctx.body能获取到正确值时,才会返回相应的内容,否则就是404的情况了。

  1. 添加中间件
function task0 (ctx, next) {
    return next()
}
function async task1 (ctx, next) {
    await next()
}
function async task3 (ctx, next) {
    await new Promise(resolve => {
        setTimeout(() => {
            ctx.body = 'hello'
        }, 1000)
    })  
}
app.use(task0);
app.use(task1);
app.use(task2);
  1. 中间件的compose处理(洋葱模型)
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)
      }
    }
  }
}

当一个http请求过来时的简易代码:

// 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(() => {
            ctx.body = 'hello'
            resolve()
        }, 1000)
    })  
}
function dispatch_3 () {};

换个方式,这样看应该比较好理解:

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

Promise.resolve(task0(ctx)).then(() => {
    console.log('res', ctx.body)
    handleResponse(ctx)
});

// res: 'hello'

如果task0 即没有return next()(返回新的Promise对象)又没有await next(),那么它会马上到handleResponse(ctx),而此时ctx.body='hello'还没有执行。

所以与express不同,koa里面的next用await next()或者return next()