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.
- 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
- 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().