JavaScript 异步编程

所谓的异步指的是函数的调用并不直接返回执行的结果,而往往是通过回调函数异步的执行。异步的主要目的是处理非阻塞,在和HTML交互的过程中,会需要一些IO操作(典型的就是Ajax请求,脚本文件加载),如果这些操作是同步的,就会阻塞其它操作,用户的体验就是页面失去了响应。avascript通过事件驱动机制,在单线程模型下,以异步回调函数的形式来实现非阻塞的IO操作。

常见的异步模型

  • 高阶函数
  • 事件监听

    1
    2
    3
    4
    5
    6
    f.on("evt", g);
    function f(){
    setTimeout(function(){
    f.trigger("evt");
    })
    }
  • 发布/订阅

    1
    2
    3
    4
    5
    6
    7
    E.subscribe("evt", g);
    function f(){
    setTimeout(function () {
      // f的任务代码
      E.publish("evt");
    }, 1000);
    }
  • Promise

Promise

传统的回调函数写法使得代码混成一团,变得横向发展而不是向下发展。Promises规范就是为了解决这个问题而提出的,目标是使用正常的程序流程(同步),来处理异步操作。

Promise把类似的异步处理对象和处理规则进行规范化, 并按照采用统一的接口来编写,采取规定方法之外的写法都会出错。promise的功能是可以将复杂的异步处理轻松地进行模式化。
简单来说,它起到代理作用(proxy),充当异步操作与回调函数之间的中介。它使得异步操作具备同步操作的接口,使得程序具备正常的同步运行的流程,回调函数不必再一层层嵌套。它的思想是,每一个异步任务立刻返回一个Promise对象,由于是立刻返回,所以可以采用同步操作的流程。这个Promises对象有一个then方法,允许指定回调函数,在异步任务完成后调用。
ECMAScript 6将其写入语言标准,因此目前JavaScript语言原生支持Promise对象。
更多Promise操作-Promise

异步操作的流程控制

1、串行执行

可以编写一个流程控制函数,让它来控制异步任务,一个任务完成以后,再执行另一个。这就叫串行执行

1
2
3
4
5
6
7
8
9
10
11
12
13
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
function series(item) {
if(item) {
async( item, function(result) {
results.push(result);
return series(items.shift());
});
} else {
return final(results);
}
}
series(items.shift());

函数series就是串行函数,它会依次执行异步任务,所有任务都完成后,才会执行final函数。items数组保存每一个异步任务的参数,results数组保存每一个异步任务的运行结果

2、并行执行

1
2
3
4
5
6
7
8
9
10
11
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];

items.forEach(function(item) {
async(item, function(result){
results.push(result);
if(results.length == items.length) {
final(results);
}
})
});

上面代码中,forEach方法会同时发起6个异步任务,等到它们全部完成以后,才会执行final函数。

并行执行的好处是效率较高,比起串行执行一次只能执行一个任务,较为节约时间。但是问题在于如果并行的任务较多,很容易耗尽系统资源,拖慢运行速度。因此有了第三种流程控制方式

3、并行与串行的结合
所谓并行与串行的结合,就是设置一个门槛,每次最多只能并行执行n个异步任务。这样就避免了过分占用系统资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
var running = 0;
var limit = 2;

function launcher() {
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) {
final();
}
});
running++;
}
}

launcher();

JavaScript 多线程

JavaScript 中的 Worker 对象可以创建一个独立线程来处理数据,很自然的处理了阻塞问题。我们可以把繁重的计算任务交给 Worker,等他处理完了再把数据 Post 过来。

Worker 是一个有用的工具,可以创建 ajax 对象和 WebSocket 对象,也就是说他可以直接向服务器请求数据。不过他不能访问 DOM 的信息,更不能直接处理 DOM。

更多的worker-worker

Generator 对象解决异步

参考:https://segmentfault.com/a/1190000002545312
http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html