# 深入理解Promise
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。
从语法上讲,promise是一个对象,从它可以获取异步操作的消息;
从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。
创造promise实例后,它会立即执行。
# 传统的回调有五大信任问题
- 调用回调过早
- 调用回调过晚(或没有被调用)
- 调用回调次数过少或过多
- 未能传递所需的环境和参数
- 吞掉可能出现的错误和异常
promise解决信任问题 (opens new window)
# Promise
对象特点
- 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。
# promise 缺点
- 首先,无法取消promise, 一旦新建它就会立即执行,无法中途取消
- 其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
- 第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
语法参考:Promise API (opens new window)
# 链式调用*
then方法必定会返回一个新的Promise
可理解为
后续处理也是一个任务
新任务的状态取决于后续处理:
若没有相关的后续处理,新任务的状态和前任务一致,数据为前任务的数据
若有后续处理但还未执行,新任务挂起。
若后续处理执行了,则根据后续处理的情况确定新任务的状态
- 后续处理执行无错,新任务的状态为完成,数据为后续处理的返回值
- 后续处理执行有错,新任务的状态为失败,数据为异常对象
- 后续执行后返回的是一个任务对象,新任务的状态和数据与该任务对象一致
Promise.resolve(1).then(2).then(Promise.resolve(3)).then(console.log);
// 如果then函数中不是一个函数,会被替换成 `(x)=>x` 的形式
// 上面的代码就相当于
Promise.resolve(1).then(function (x) {
return x;
}).then(function (x) {
return x;
}).then(console.log);
// 就相当于
Promise.resolve(1).then(console.log);
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 并发任务控制
function timeout(time){
return new Promise(resolve =>{
setTimeout(() => {
resolve()
}, time)
})
}
// 根据以下输出,完成任务调度类
class SuperTask{
constructor(parallelCount = 2){
// 最大任务并行数
this.parallelCount = parallelCount;
this.tasks = [];
this.runningCount = 0;
}
add(task){
return new Promise((resolve, reject) =>{
this.tasks.push({task, resolve, reject});
this._run();
})
}
_run(){
while(this.runningCount < this.parallelCount && this.tasks.length){
const {task, resolve, reject} = this.tasks.shift();
this.runningCount++;
Promise.resolve(task()).then(resolve, reject).finally(() =>{
this.runningCount--;
this._run();
});
}
}
}
const superTask = new SuperTask();
function addTask(time, name){
superTask.add(() => timeout(time)).then(() => {
console.log(`任务${name}完成`);
})
}
addTask(10000, 1); // 10000ms后输出: 任务1完成
addTask(5000, 2); // 5000ms后输出: 任务2完成
addTask(3000, 3); // 8000ms后输出: 任务3完成
addTask(4000, 4); // 12000ms后输出: 任务4完成
addTask(5000, 5); // 15000ms后输出: 任务5完成
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47