JS异步操作的流程控制

如果有多个异步操作,就会存在一个流程控制的问题,如何保证代码按照自己想要的流程执行下去。

例如:下面是一个异步任务,非常耗时,每次需要1秒才能完成,然后再调用回调函数。

function async(arg, callBack) {
  console.log('参数为:' + arg +' , 1秒后返回结果');
  setTimeout(function () { callBack(arg * 2); }, 1000);
}

如果有五个这样的异步任务,需要全部执行完成后才能执行任务 end 函数,怎么安排?

function end(value) {
  console.log('完成: ', value);
}

async(1, function (value) {
  async(2, function (value) {
    async(3, function (value) {
      async(4, function (value) {
         async(5, final);
      });
    });
  });
});
// 参数为 1 , 1秒后返回结果
// 参数为 2 , 1秒后返回结果
// 参数为 3 , 1秒后返回结果
// 参数为 4 , 1秒后返回结果
// 参数为 5 , 1秒后返回结果
// 完成:  10

正常同步写法为嵌套,完成全部任务需要5秒的时间。缺点:容易出错,代码可阅读性差。

串联执行

我们可以写一个流程控制函数,让它来控制异步任务,一个任务完成后再执行下一个任务,这叫做串联执行。

ar items = [ 1, 2, 3, 4, 5];
var results = [];

function async(arg, callback) {
  console.log('参数为: ' + arg +' , 1秒后返回结果');
  setTimeout(function () { callback(arg * 2); }, 1000);
}

function end(value) {
  console.log('完成: ', value);
}

function series(item) {
  if(item) {
    async( item, function(result) {
      results.push(result);
      return series(items.shift());
    });
  } else {
    return end(results[results.length - 1]);
  }
}

series(items.shift());

series函数就是串联执行函数,它会依次执行async异步任务,所有异步任务完成后才会执行end函数。items保存每一个异步任务的参数,results保存异步任务完成后的结果。缺点:整个异步任务执行完成需要5秒。

并联执行

我们可以修改一下代码,让异步任务并联执行,这样所有的异步任务可以同时执行,当所以异步任务执行完成后再执行end函数,以下代码只需一秒便可执行end函数:

var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];

function async(arg, callback) {
  console.log('参数为: ' + arg +' , 1秒后返回结果');
  setTimeout(function () { callback(arg * 2); }, 1000);
}

function end(value) {
  console.log('完成: ', value);
}

items.forEach(function(item) {
  async(item, function(result){
    results.push(result);
    if(results.length === items.length) {
      end(results[results.length - 1]);
    }
  })
});

缺点:同时执行多个异步任务,有可能消耗大量性能,拖慢运行速度。如果我们能控制每次执行异步任务的数量,做到并联执行与串联执行的平衡,那岂不是更好?

并联与串联的结合

并联与串联执行结合的意思就是设置一个条件,每次只能n个异步请求,这样就能控制异步任务对系统资源的消耗。

var items = [ 1, 2, 3, 4, 5];
var results = [];
var running = 0;
var limit = 2;

function async(arg, callback) {
  console.log('参数为: ' + arg +' , 1秒后返回结果');
  setTimeout(function () { callback(arg * 2); }, 1000);
}

function end(value) {
  console.log('完成: ', value);
}

function go() {
  while(running < limit && items.length > 0) {
    var item = items.shift();
    async(item, function(result) {
      results.push(result);
      running--;
      if(items.length > 0) {
        launcher();
      } else if(running == 0) {
        end(results);
      }
    });
    running++;
  }
}

go();

上面的go函数中,最多同时运行两个异步任务,变量running记录正在执行的任务数量,当小于条件(这里条件为:running<2)时启动下一个异步任务,如果running等于0,表示所有异步任务都执行完成,最后执行end函数。

这段代码每次同时进行2个异步任务,通过limit变量调节每次执行异步任务的数量,做到了串联与并联、效率与资源的平衡 。

原文引自阮一峰老师:异步操作概述

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值