最近研究了一下 koa1 和 koa2 的源码,其中比较重要的就是中间件的流程控制,其中 koa1 是通过 generator 实现中间件流程控制,koa2 则是通过 async 函数,说起来利用 js 实现异步流程控制的方法也不少了,所以做一下总结对比。主要总结了五种实现方式:

  • callback
  • promise
  • async
  • thunk 版 generator
  • promise 版 generator

具体代码可以看我在 codepen 上的 demo,分别打开各方法注释即可。

See the Pen async by newming (@newming) on CodePen.

下面简单看一下最终各中方式实现代码:

callback

1
2
3
4
5
6
7
8
9
10
11
12
13
animate(ball1, 100, function () {
  animate(ball2, 200, function () {
    animate(ball3, 300, function () {
      animate(ball3, 150, function () {
        animate(ball2, 150, function () {
          animate(ball1, 150, function () {

          })
        })
      })
    })
  })
})

promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
promiseAnimate(ball1,100)
  .then(function () {
    return promiseAnimate(ball2,200)
  })
  .then(function () {
    return promiseAnimate(ball3,300)
  })
  .then(function () {
    return promiseAnimate(ball3,150)
  })
  .then(function () {
    return promiseAnimate(ball2,150)
  })
  .then(function () {
    return promiseAnimate(ball1,150)
  })

async

1
2
3
4
5
6
7
(async function () {
  await promiseAnimate(ball1, 100)
  await promiseAnimate(ball2, 200)
  await promiseAnimate(ball3,150)
  await promiseAnimate(ball2,150)
  await promiseAnimate(ball1,150)
})()

基于 promise 的 generator 实现

1
2
3
4
5
6
7
8
var movePromise = function* (){
  yield promiseAnimate(ball1, 100);
  yield promiseAnimate(ball2, 200);
  yield promiseAnimate(ball3, 150);
  yield promiseAnimate(ball2, 150);
  yield promiseAnimate(ball1, 150);
};
run(movemovePromise) // 需要自己实现 run 函数

基于 thunk 的 generator 实现

1
2
3
4
5
6
7
8
9
var moveThunk = function* (){
  yield animateThunk(ball1, 100)
  yield animateThunk(ball2, 200)
  yield animateThunk(ball3, 150)
  yield animateThunk(ball2, 150)
  yield animateThunk(ball1, 150)
};

run(moveThunk) // 同样需要自己 run 函数

总结

callback 写法最不直观,当有多个连续执行异步操作任务时,容易造成回调地狱。其余四种都是同步写法实现异步流程控制,其中 async 函数相当于自动执行的 Promise 以及后边的 generator 函数,如果使用 generator,需要自己实现一个 run 函数来进行自动化执行,这里就要提到著名的 co 库。总的来说还是 async 不错。

另外,能力有限,可能存在表达错误。另外具体各种方法实现,这里不做详细说明,可以参考下边提到的参考文章。

参考文章: