promise源码

API结构图

image.png

术语

image.png

promise的执行过程

image.png
了解Promise诞生的历史背景
学会使用Promise解决异步回调带来的问题
掌握Promise的进阶用法

异步操作的常见语法

1
2
3
4
5
6
7
document.getElementById('start').addEventListener('click',start,false);

function start(){
//响应事件,进行响应的操作
}

$('#start').on('click',start);

image.png
有了Node.js之后
对异步的依赖进一步加剧了……
无阻塞高并发

Promise详解

1
2
3
4
5
6
7
8
9
10
11
12
new Promise(
//执行器 executor
function(resolve,reject){
// 一段耗时很长的异步操作
resolve();// 数据处理完成
reject();// 数据处理出错
}
).then(function A(){
//成功,下一步
},function B(){
//失败,做相应处理
})

Promise实例一经创建,执行器立即执行

image.png

简单实例

1
2
3
4
5
6
7
8
console.log("here we go");
new Promise(resolve => {
setTimeout(() => {
resolve("hello");
}, 2000);
}).then(value => {
console.log(`${value} world`);
});

两步执行的范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
console.log("here we go");
new Promise(resolve => {
setTimeout(() => {
resolve("hello");
}, 2000);
})
.then(value => {
console.log(value);
return new Promise(resolve => {
setTimeout(() => {
resolve("nihao");
}, 1000);
});
})
.then(value => {
console.log(`${value} world`);
});
here we go
hello
nihao world

对已完成的Promise执行then

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
console.log("start");

let promise = new Promise(resolve => {
setTimeout(() => {
console.log("the promise fulfilled");
resolve("hello,world");
}, 1000);
});

setTimeout(() => {
promise.then(value => {
console.log("value:", value);
});
}, 0);

start
the promise fulfilled
value: hello,world

不管promise前面的状态是否完成,都会按它的队列去执行

then里不返回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
console.log("start");

let promise = new Promise(resolve => {
setTimeout(() => {
console.log("the promise fulfilled");
resolve("hello,world");
}, 2000);
})
.then(value => {
console.log(value);
console.log("everyone");
(function() {
console.log("比包");
return new Promise(resolve => {
setTimeout(() => {
console.log("Mr Lanrence");
resolve("Merry Xmas");
}, 0);
});
})();
return false;
})
.then(value => {
console.log(value + " world");
});
start
the promise fulfilled
hello,world
everyone
比包
false world
Mr Lanrence

引出.then()

  • .then()接受两个函数作为参数,分别代表fulfilled和rejected

  • .then()返回一个新的Promise实例,所以它可以链式调用

  • 当前面的Promise状态改变时,.then()根据其最终状态,选择特定的状态响应函数执行

  • 状态响应函数可以返回新的Promise,或其他值

  • 如果返回新的Promise,那么下一级.then()会在新Promise状态改变后执行

  • 如果返回其它任何值,则会立刻执行下一级.then()

    .then()里有.then()的情况

  • 因为.then()返回的还是Promise实例。

  • 会等里面的.then()执行完,再执行外面的。

  • 对于我们来说,此时最好将其展开,会更好读。

    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
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    console.log("start");

    let promise = new Promise(resolve => {
    console.log("step1");
    setTimeout(() => {
    resolve("100");
    }, 1000);
    })
    .then(value => {
    return new Promise(resolve => {
    console.log("Step 1-1");
    setTimeout(() => {
    resolve(110);
    }, 1000);
    })
    .then(value => {
    console.log("Step 1-2");
    return value;
    })
    .then(value => {
    console.log("Step 1-3");
    return value;
    });
    })
    .then(value => {
    console.log(value);
    console.log("Step 2");
    });
    start
    step1
    Step 1-1
    Step 1-2
    Step 1-3
    110
    Step 2


    一样的效果可以这样改
    console.log("start");

    let promise = new Promise(resolve => {
    console.log("step1");
    setTimeout(() => {
    resolve("100");
    }, 1000);
    })
    .then(value => {
    return new Promise(resolve => {
    console.log("Step 1-1");
    setTimeout(() => {
    resolve(110);
    }, 1000);
    });
    })
    .then(value => {
    console.log("Step 1-2");
    return value;
    })
    .then(value => {
    console.log("Step 1-3");
    return value;
    })
    .then(value => {
    console.log(value);
    console.log("Step 2");
    });

    start
    step1
    Step 1-1
    Step 1-2
    Step 1-3
    110
    Step 2

问题:下面的四种Promise的区别是什么

问题一

image.png

问题二

image.png

问题三(比较有欺骗性)doSomethingElse()也是一个promise

image.png

问题四

image.png

错误处理

1
2
3
4
5
6
7
8
9
10
11
12
console.log("start");
new Promise(resolve => {
setTimeout(() => {
throw new Error("bye");
}, 2000);
})
.then(value => {
console.log(value + " world");
})
.then(error => {
console.log("Error: ", error, error.message);
});

image.png

错误处理的两种做法:

  • reject(‘错误信息’).then(null,message=>{})
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
console.log("start");
new Promise((resolve, reject) => {
setTimeout(() => {
reject("bye bye");
}, 2000);
}).then(
value => {
console.log(value + " world");
},
value => {
console.log("Error: ", value);
}
);

一样的

console.log("start");
new Promise((resolve, reject) => {
setTimeout(() => {
// reject("bye bye");
throw new Error("bye bye");
}, 2000);
}).then(
value => {
console.log(value + " world");
},
value => {
console.log("Error: ", value);
}
);

image.png

  • throw new Error(‘错误信息’).catch(message=>{})

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    console.log("start");
    new Promise((resolve, reject) => {
    setTimeout(() => {
    throw new Error("bye");
    }, 2000);
    })
    .then(value => {
    console.log(value + " world");
    })
    .then(error => {
    console.log("Error: ", error, error.message);
    });
  • 推荐使用第二种,更加清晰好读,并且可以捕获前面的错误

  • 强烈建议在所有队列最后都加上.catch(),以避免漏掉错误处理造成意想不到的问题

image.png

Promise.all

  • 当所有子Promise都完成,该Promise完成,返回值是全部值的数组
  • 有任何一个失败,该Promise失败,返回值是第一个失败的子Promise的结果

    与.map连用、实现队列

    image.png

Promise.resolve

promise then里面的值,都是resolve来传递改变的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
console.log("start");
Promise.resolve()
.then(value => {
console.log("step 1", value);
return Promise.resolve("Hello");
})
.then(value => {
console.log(value, "world");
return Promise.resolve(
new Promise(resolve => {
setTimeout(() => {
resolve("Good");
}, 2000);
})
);
})
.then(value => {
console.log(value, "evening");
return Promise.resolve({
then() {
console.log(", everyone");
}
});
});

Promise.reject

目前位置就是提示报错,没有别的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let promise = Promise.reject("something wrong");
promise
.then(() => {
console.log("it's ok ");
})
.catch(() => {
console.log("no,it's not ok");

return Promise.reject({
then() {
console.log("it will be ok");
},
catch() {
console.log("not yet");
}
});
});

Promise.race

它有任意一个完成就算完成了和promise.all的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let p1 = new Promise(resolve => {
setTimeout(() => {
resolve("1111111");
}, 10000);
});
let p2 = new Promise(resolve => {
setTimeout(() => {
resolve("2222222");
}, 2000);
});

Promise.race([p1, p2]).then(value => {
console.log(value);
});

Promise.all([p1, p2]).then(value => {
console.log(value);
});


2222222
[ '1111111', '2222222' ]

常见用法:
把异步操作和定时器放在一起,如果定时器先触发,就认为超时,告知用户

把回掉包装成Promise

可读性更好
返回的结果可以加入任何promise队列

手撕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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104

function Promise(executor) {
let self = this;
// 保存成功的值和失败的原因
self.value = undefined;
self.reason = undefined;
// 专门存成功的回调
self.onResolvedCallbacks = [];
// 专门存失败的回调
self.onRejectedCallbacks = [];
// 保存一下当前这个promise的状态(promise有三个状态)
self.status = "pending";
function resolve(value) {
if (self.status === "pending") {
self.value = value;
self.status = "resolved";
self.onResolvedCallbacks.forEach(function(fn) {
fn();
});
}
}
function reject(reason) {
if (self.status === "pending") {
self.reason = reason;
self.status = "rejected";
self.onRejectedCallbacks.forEach(function(fn) {
fn();
});
}
}
try {
executor(resolve, reject);
} catch (e) {
// 如果执行执行器时发生异常那就走到then失败的函数中
reject(e);
}
// executor是立即执行的
}
// 解析链式调用的 (他还要和其他的promise进行结合)
function resolvePromise(x, promise2, resolve, reject) {
if (x === promise2) {
// 自己不能等待自己完成
return reject(new TypeError("循环引用"));
}
// 如果x是一个函数 或者x是一个对象 就有可能x是一个promise
if (x !== null && (typeof x === "function" || typeof x === "object")) {
try {
let then = x.then;
if (typeof then == "function") {
// 是promise
then.call(
x,
function(y) {
resolve(y);
},
function(r) {
reject(r);
}
);
} else {
// {then:123}
resolve(x);
}
} catch (e) {
// 如果x取then的时候可能会发生异常,如果有异常
reject(e);
}
} else {
// 普通值的情况直接成功即可
resolve(x);
}
}

// then方法中需要传递两个参数 分别是成功的回调和失败的回调
Promise.prototype.then = function(onFulfilled, onRejected) {
let self = this;
// 调用then后返回一个promise
let promise2 = new Promise(function(resolve, reject) {
if (self.status === "resolved") {
// 我们限制需要做的事情就是把then中成功或者失败后函数执行的结果获取到
// 看一看是不是promise 如果是promise 就让promise执行,取到最终这个promise的执行结果 ,让返回的promise 成功或者失败
// 如果x是普通值就让这个返回的promise 变成成功态
let x = onFulfilled(self.value);
resolvePromise(x, promise2, resolve, reject);
}
if (self.status === "rejected") {
let x = onRejected(self.reason);
resolvePromise(x, promise2, resolve, reject);
}
// executor中有异步操作,此时调用then时 处于等待态
if (self.status === "pending") {
self.onResolvedCallbacks.push(function() {
let x = onFulfilled(self.value);
resolvePromise(x, promise2, resolve, reject);
});
self.onRejectedCallbacks.push(function() {
let x = onRejected(self.reason);
resolvePromise(x, promise2, resolve, reject);
});
}
});
return promise2;
};
module.exports = Promise;

Promise 的状态

一个 Promise 的当前状态> 必须为以下三种状态中的一种:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。

  • 等待态(Pending)

处于等待态时,promise 需满足:可以迁移至执行态或拒绝态

  • 执行态(Fulfilled)

处于执行态时,promise 需满足:不能迁移至其他任何状态,必须拥有一个不可变终值

  • 拒绝态(Rejected)

处于拒绝态时,promise 需满足:不能迁移至其他任何状态,必须拥有一个不可变据因
这里的不可变指的是恒等(即可用 > === 判断相等),而不是意味着更深层次的不可变( 指当 value 或 reason 不是> 基本值时,只要求其引用地址相等,但属性值可被更改)。

把任意异步操作包装成 Promise

Fetch

async/await