newming的博客

个人学习、总结文章

js五种异步实现方式对比

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

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

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

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>async</title>

  <style>
    .ball {
      width: 40px;
      height: 40px;
      margin-bottom: 20px;
      border-radius: 50%;
    }

    .ball1 {
      background-color: red;
    }

    .ball2 {
      background-color: teal;
    }

    .ball3 {
      background-color: deeppink;
    }
  </style>
</head>

<body>
  <h1>五种异步流程控制的实现</h1>

  <div class="ball ball1" style="margin-left:0;"></div>
  <div class="ball ball2" style="margin-left:0;"></div>
  <div class="ball ball3" style="margin-left:0;"></div>

  <script>
    var ball1 = document.querySelector('.ball1');
    var ball2 = document.querySelector('.ball2');
    var ball3 = document.querySelector('.ball3');

    // 回调函数版的动画
    function animate(ball, distance, cb) {
      setTimeout(function () {
        var marginLeft = parseInt(ball.style.marginLeft, 10);
        if (marginLeft == distance) {
          cb && cb('德玛西亚');
        } else {
          if (marginLeft < distance) {
            marginLeft++;
          } else {
            marginLeft--
          }
          ball.style.marginLeft = marginLeft + 'px';
          animate(ball, distance, cb);
        }
      }, 13)
    }
    // promise 版的动画实现
    function promiseAnimate(ball, distance) {
      return new Promise(function (resolve, reject) {
        function _animate() {
          setTimeout(function () {
            var marginLeft = parseInt(ball.style.marginLeft, 10);
            if (marginLeft == distance) {
              resolve()
            } else {
              if (marginLeft < distance) {
                marginLeft++;
              } else {
                marginLeft--
              }
              ball.style.marginLeft = marginLeft + 'px';
              _animate()
            }
          }, 13)
        }
        _animate()
      })
    }

    //方法一,利用回调函数实现
    // 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实现
    // 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 await 实现
    // (async function () {
    //   await promiseAnimate(ball1, 100)
    //   await promiseAnimate(ball2, 200)
    //   await promiseAnimate(ball3,150)
    //   await promiseAnimate(ball2,150)
    //   await promiseAnimate(ball1,150)
    // })()

    // 方法四 基于 promise 的 generator 实现
    // function run(gen){
    //   var g = gen();

    //   function next(data){
    //     var result = g.next(data);
    //     if (result.done) return result.value;
    //     result.value.then(function(data){
    //       next(data);
    //     });
    //   }

    //   next();
    // }

    // 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)

    // 方法五 基于 thunk 的 generator 实现
    function thunkify(fn) {
      return function () {
        var args = new Array(arguments.length);
        var ctx = this;

        for (var i = 0; i < args.length; ++i) {
          args[i] = arguments[i];
        }

        return function (done) {
          var called;

          args.push(function () {
            if (called) return;
            called = true;
            done.apply(null, arguments);
          });

          try {
            fn.apply(ctx, args);
          } catch (err) {
            done(err);
          }
        }
      }
    };

    var animateThunk = thunkify(animate)

    function run(fn) {
      var gen = fn();

      function next(value) {
        console.log(value)
        var result = gen.next();
        if (result.done) return;
        result.value(next);
      }

      next();
    }

    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)
  </script>

</body>

</html>

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

callback

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

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

(async function () {
  await promiseAnimate(ball1, 100)
  await promiseAnimate(ball2, 200)
  await promiseAnimate(ball3,150)
  await promiseAnimate(ball2,150)
  await promiseAnimate(ball1,150)
})()

基于 promise 的 generator 实现

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 实现

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 不错。

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

参考文章:

阅读更多
文章标签: js async nodejs koa
个人分类: javascript
想对作者说点什么? 我来说一句

js对象,五种继承 详细讲解

2011年03月11日 910KB 下载

没有更多推荐了,返回首页

关闭
关闭