1
2
3
4
5
6
7
8
9
setTimeout(function() {
console.log("拉数据");
setTimeout(function() {
console.log("做这个那个,再发数据");
setTimeout(function() {
console.log("数据发送成功,恭喜充值成功游戏时间 +1s!");
},3000)
},2000)
},1000)

噢,亲爱的,你一定去过地狱,是吗?

欢迎来到异步世界

当世界没有异步运算的时候,程序顺序执行,简单明了。有一天来了一个异步运算——拉数据

1
2
3
4
5
6
7
var data = 0;
function getData() {
setTimeout(function (){
data = 1;
console.log("数据拉取成功");
},1000);
}

拉完数据自然要处理。

1
2
3
4
function handleData(){
data += 1;
console.log("处理数据完成");
}

按照顺序逻辑编写代码

1
2
3
4
getData();
handleData();
// => 处理数据完成
// => 数据拉取成功

天呐,居然先处理了数据才拉取数据。这是自然的。因为计算是异步的,所以不得不把所有跟数据有关的函数移到异步函数内部。

1
2
3
4
5
6
7
8
9
10
11
12
13
var data = 0;
function getData() {
setTimeout(function (){
data = 1;
console.log("数据拉取成功");
handleData();
},1000);
}

function handleData(){
data += 1;
console.log("处理数据完成");
}

运行时候的代码

1
2
3
getData()
// => 数据拉取成功
// => 处理数据完成

拉玩还不算,还得发数据。做完一次人肉 CPS 变换后代码变成这样

1
2
3
4
5
6
7
8
9
10
11
function getData(){
setTimeout(function() {
console.log("拉数据");
setTimeout(function() {
console.log("做这个那个,再发数据");
setTimeout(function() {
console.log("数据发送成功,恭喜充值成功游戏时间 +1s!");
},3000)
},2000)
},1000)
}

什么?你觉得代码还好?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
setTimeout(function(){
setTimeout(function(){
setTimeout(function(){
setTimeout(function(){
setTimeout(function(){
setTimeout(function(){
setTimeout(function(){
setTimeout(function(){
setTimeout(function(){
setTimeout(function(){
setTimeout(function(){
console.log("终于做完了")
},1000)
},1000)
},1000)
},1000)
},1000)
},1000)
},1000)
},1000)
},1000)
},1000)
},1000)

欢迎来到回调地狱。

未来的承诺

很长一段时间不少人都写着这种代码,终于 ES 6 美好的承诺 Promise 来临。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function getData(callback) {
setTimeout(function (){
var data = 0;
console.log("数据拉取成功");
callback(data);
},1000);
console.log("拉取中……")
}

function handleData(data) {
return new Promise(function(resolve){
console.log("处理中……");
setTimeout(function(){
data += 1;
console.log("处理数据完成");
resolve(data);
},2000);
});
}

function sendData(data) {
return new Promise(function(resolve){
console.log("发送中……");
setTimeout(function(){
console.log("数据发送成功");
resolve(data);
},3000);
});
}

function showSucess(data){
console.log("恭喜充值成功游戏时间 +" + data + "s!");
}

这么运行

1
2
3
4
5
var async = new Promise(function(resolve){
getData(resolve)
});

async.then(handleData).then(sendData).then(showSucess);

除了写法上更直观外,还可以使用 .catch() 捕获异常。

还可以使用 .all() 当所有异步完成时触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var p1 = new Promise(function(resolve){
setTimeout(function(){
console.log("p1");
resolve();
},1000)
})

var p2 = new Promise(function(resolve){
setTimeout(function(){
console.log("p2");
resolve();
},2000)
})

var p3 = new Promise(function(resolve){
setTimeout(function(){
console.log("p3");
resolve();
},3000)
})

Promise.all([p1,p2,p3]).then(function(){
console.log("全部执行完毕")
});

使用 .race() 来竞争,当有异步完成时触发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var p1 = new Promise(function(resolve){
setTimeout(function(){
console.log("p1");
resolve("p1");
},Math.random() * 1000)
})

var p2 = new Promise(function(resolve){
setTimeout(function(){
console.log("p2");
resolve("p2");
},Math.random() * 1000)
})

var p3 = new Promise(function(resolve){
setTimeout(function(){
console.log("p3");
resolve("p3");
},Math.random() * 1000)
})

Promise.race([p1,p2,p3]).then(function(winner){
console.log(winner + "优先完成");
})

最后

Promise 也是一个 Monad (逃