js五种异步实现方式对比

原创 2018年04月15日 21:02:20

最近研究了一下 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 不错。

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

参考文章:

深入Javascript Function(函数)视频教程

-
  • 1970年01月01日 08:00

JS实现异步编程的几种方式

转载出处:http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html Javascript异步编程...
  • sdfujichao
  • sdfujichao
  • 2016-08-09 09:57:40
  • 2419

原生javascript实现异步的7种方式

原生的javascript 实现异步的方式其实远远不至7种, 大可以分3类, 延迟类型:setTimeout(setInterval也是可以的)、requestAnimationFrame、set...
  • zakkye
  • zakkye
  • 2015-05-21 13:36:53
  • 4125

异步调用实现对比 java

异步调用主要用于当前程序的执行不用等待调用方法执行结束就可以继续执行。用一个最简单的例子来说,当前的方法要调用一个发送短信的方法,但是发送短信的方法调用了外部的接口,这样就导致短信发送方法耗费的时间很...
  • u014120765
  • u014120765
  • 2014-07-06 22:13:17
  • 1149

java多线程详解三多线程5种实现方式

java种多线程实现方式主要有5种,继承Thread类,实现Runnable接口、实现Callable和 FutureTask包装器来创建Thread线程、使用ExecutorService、Call...
  • qzqanzc
  • qzqanzc
  • 2017-02-19 09:53:45
  • 126

JS异步加载的三种方式

JS同步加载,异步加载,延迟加载,图片懒加载
  • l522703297
  • l522703297
  • 2016-02-27 12:39:07
  • 51192

Linux五种IO模型性能分析

socket阻塞与非阻塞,同步与异步 作者:huangguisu 1. 概念理解      在进行网络编程时,我们常常见到同步(Sync)/异步(As...
  • jay900323
  • jay900323
  • 2014-01-11 15:53:55
  • 25806

Nodejs中控制异步的几种方法【陆续更新……】

1.异步回调原生写法 serverClient.get("http://localhost:3001/test/test.php?page=1&page_count=1", function (er...
  • geishel
  • geishel
  • 2017-03-21 19:18:19
  • 245

JavaScript 异步提交表单的6种方式

零、在学习异步提交表单之前,先来学习几个JQuery方法和属性 1、serialize():序列表格内容为字符串。如下: queryBean.orderBy=OPERATE_TIME&que...
  • liupeifeng3514
  • liupeifeng3514
  • 2018-01-06 13:01:20
  • 583

JS异步加载的几种方式

1、同步加载我们平常写JS的时候都是用的阻塞模式如&amp;lt;script type=&quot;text/javascript&quot; src=&quot;../../libs/crypto...
  • shan1991fei
  • shan1991fei
  • 2018-02-28 09:55:20
  • 32
收藏助手
不良信息举报
您举报文章:js五种异步实现方式对比
举报原因:
原因补充:

(最多只允许输入30个字)