「译」理解 JavaScript 中的 Promise

作者:Gokul N K
原文链接:Understanding promises in JavaScript

我对 JavaScript 的感觉是“又爱又恨”。但是它还是一直吸引着我。我做了 10 年的 Java 和 PHP 开发,相较之下,JavaScript 是一个非常独特而有趣的存在。现在我正努力花时间来了解更多的 JavaScript 内容。

我遇到的第一个有意思的主题就是 Promise。曾经不止一次的听人们说起 Promise 把他们从回调地狱中拯救出来。而这只不过是 Promise 顺带的一点好处而已,Promise 包含了更多的内容,直到现在我才把它搞清楚。这是一篇长文,如果你想做些高亮标记什么的,你可以试试我们这个扩展 http://bit.ly/highlights-extension

背景

第一次使用 JavaScript 的时候你可能会觉得很受挫。你可能会听到一些人说 JavaScript 是同步编程语言而另一些人则说它是异步的。你会听到诸如阻塞代码、非阻塞代码、事件驱动设计模式、事件生命周期、函数栈、事件队列、冒泡、polyfill、babel、angular、reactJS、VueJS 以及一大堆的工具和库。别担心,你不是第一个感觉头昏脑胀的人。有一个专门的名词,叫做 JavaScript Fatigue(JS 疲乏)。这条推特总结的很到位。

JS 疲乏指的是人们用他们不需要的工具去解决不存在的问题

如果你想了解关于 JS 疲乏的更多细节,可以阅读下面这篇文章。这篇文章能在 Hackernoon 上获得 42000 次鼓掌不是没有原因的 :)

在 2016 年学习 JavaScript 的感受

JavaScript 是一个同步型的编程语言。不过借助于回调函数,我们可以让它像异步编程语言一样执行。

让门外汉理解 Promise

JavaScript 中的 Promise 跟现实生活中的 Promise 非常像。因此我们先来看一看现实生活中的承诺(Promise)是怎样的。

字典中是这样描述 Promise 的:

Promise: 名词:某人保证会做某事或则某件事一定会发生。

那么当别人对你做了承诺(Promise)会发生什么呢?

  1. Promise 是对某事一定会发生的保证。不论是(作出保证)的这个人亲自去做还是交由第三方去完成。然后你就可以在这个保证的基础上做下一步的计划。
  2. Promise 会被实现或是没能实现。
  3. 当 Promise 实现时,会带回来某种结果。这个结果可被用于下一步的行动。
  4. 如果一个 Promise 未能成功实现,你会希望了解为何这个做出承诺的人未能履行他的诺言。一旦你了解并确认了 Promise 执行失败的原因,你就可以计划接下来如何处理它了。
  5. 在做出 Promise 的时候,我们得到的只是一个保证。除此之外我们暂时什么都做不了。不过我们可以先计划一下当 Promise 被实现以后(得到了我们预期的结果)或是 未能实现(了解失败原因并制定应急预案)时要做什么。
  6. 不过也有可能这个人在跟你做完 Promise 以后就杳无音讯了。这时候你需要设置一个时间节点。比如说如果这个做出承诺的人在 10 天之内没有回来我将假设他遇上了什么意外无法完成承诺了。因此即使这个人在 15 天之后才回来也没什么大不了,因为你已经制定了备用方案。

JavaScript 中的 Promise

根据经验,我总是从 MDN Web Docs 阅读 JavaScript 的文档。我认为它最为精准权威。我通过阅读 MDN Web Docs 上的 Promise 相关页面 以及写代码实践来掌握它。

理解 Promise 分为两部分。创建 Promise处理 Promise。尽管大多数情况下我们都是在写代码处理其他库产生的 Promise,全面的去了解 Promise 还是会很有帮助的。当你迈过初学者阶段以后你会发现了解“Promise 的创建”也是很重要的。

创建 Promise

我们来看一下创建 Promise 的代码格式。

1
2
3
4
new Promise(
/* 执行者 */
function(resolve, reject) { ... }
);

构造函数接受一个名为执行者(executor)的函数作为参数。executor 函数依次接受两个函数参数:resolvereject。Promise 通常被用来简化异步操作或阻塞性代码的执行,这种操作一般出现在文件操作、API 调用、数据库调用、IO 调用等情景中。这些异步操作的初始化在 executor 函数中完成。如果异步操作成功完成,Promise 的创建者会调用 resolve 函数来返回操作结果。类似的,若因为发生了错误无法完成操作,失败原因会通过调用 reject 函数返回。

现在我们已经学会如何创建 Promise 了。我们来创建一个简单的 Promise 来帮助加深理解。

1
2
3
4
5
6
7
8
9
10
var keepsHisWord;
keepsHisWord = true;
promise1 = new Promise(function(resolve, reject) {
if (keepsHisWord) {
resolve("The man likes to keep his word");
} else {
reject("The man doesnt want to keep his word");
}
});
console.log(promise1);

每个 Promise 都有 state 和 value 两个属性

由于这个 Promise 在创建后立刻就被解决了(resolved),我们无法检测它的初始状态。所以我们再创建一个需要等待一段时间才会被 resolved 的 Promise。最简单的办法就是用 setTimeout 函数。

1
2
3
4
5
6
7
8
9
promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve({
message: "The man likes to keep his word",
code: "aManKeepsHisWord"
});
}, 10 * 1000);
});
console.log(promise2);

上面的代码创建了一个会在 10 秒钟后 resolve 的 Promise。因此我们可以在它 resolve 之前查看 Promise 状态。

Promise 被 resolve 或 reject 之前的状态

10 秒种后 Promise 被 resolve。PromiseStatusPromiseValue 也随之被更新。如你所见,我们修改了 resolve 函数以便传给它一个 JSON 对象而不仅仅是简单的文本。这是为了展现出我们可以向 resovle 函数传递不同的值。

10 秒后才会 resovle 的 Promise,它返回一个 JSON 对象promise

现在我们来看一下被驳回(reject)的 Promise。我们对 Promise 1 做一点小小的修改。

1
2
3
4
5
6
7
8
9
keepsHisWord = false;
promise3 = new Promise(function(resolve, reject) {
if (keepsHisWord) {
resolve("The man likes to keep his word");
} else {
reject("The man doesn't want to keep his word");
}
});
console.log(promise3);

由于我们创建了一个未处理的 rejection,Chrome 浏览器会显示一个错误。目前你可以先把它忽略,我们后面会处理这个错误。

Promise 中的 rejection

我们看到,PromiseStatus 可以有不同的值:pendingresolvedrejected。在 Promise 创建时,PromiseStatus 处于 pending 状态,并且 PromiseValue 的值在 Promise 被 resolvedrejected 之前一直都是 undefined。当一个 Promise 处于 resolvedrejected 状态时,我们称这个 Promise 被解决了(settled)。所以通常来说,Promise 会从 pending 状态变成 settled 状态。

现在我们知道 Promise 是如何创建的了,我们可以看一下如何处理 Promise。这儿会手把手的教你理解 Promise 对象。

理解 Promise 对象

据 MDN 文档

Promise 对象代表一个最终会实现(或失败)的异步操作,以及这个操作的返回值。

Promise 对象拥有静态方法以及 原型方法。Promise对象中的静态方法可以独立应用,而原型方法需要应用于Promise对象的实例。记住普通方法和原型方法都是返回一个 Promise 会对你的学习有所帮助。

原型方法

我们先从三个原型方法开始。重申一下,所有这些方法都可以用于 Promise 对象的实例而且所有这些方法都会依次返回一个 Promise。前面我们已经看到,Promise 在刚创建时是处于 pending 状态。在 Promise 被解决(settled)以后,根据它的状态是 fulfilledrejected,下面的某一个方法会被执行。

Promise.prototype.catch(onRejected)

Promise.prototype.then(onFulfilled, onRejected)

Promise.prototype.finally(onFinally)

下面的图片展示了 thencatch 方法的流程。图片中也显示了由于这些方法返回的是一个 Promise 因此可以被继续链下去。当在 Promise 上应用了 .finally 时,无论这个 Promise 的解决状态是 fulfilled 还是 rejected,它都会执行。

Konstantin Rouda 指出 finally 得到的支持非常有限,因此在使用它时请无比做好检测。


来源:https://mdn.mozillademos.org/files/15911/promises.png

讲个小故事。你是一个上学的孩子,正在跟你妈要一部手机。她说:“月底前我就给你买一部。”

我们来看一下如何用 JavaScript 来描述这个诺言在月底前被执行。

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
var momsPromise = new Promise(function(resolve, reject) {
momsSavings = 20000;
priceOfPhone = 60000;
if (momsSavings > priceOfPhone) {
resolve({
brand: "iphone",
model: "6s"
});
} else {
reject("We donot have enough savings. Let us save some more money.");
}
});

momsPromise.then(function(value) {
console.log("Hurray I got this phone as a gift ", JSON.stringify(value));
});

momsPromise.catch(function(reason) {
console.log("Mom coudn't buy me the phone because ", reason);
});

momsPromise.finally(function() {
console.log(
"Irrespecitve of whether my mom can buy me a phone or not, I still love her"
);
});

输出结果为:

mom 未实现的承诺

如果我们把 momsSavings 的值设置为 200000,那么妈妈就有足够的钱给儿子买礼物了。这时输出如下:

mom 实现了她的承诺

我们来假装是这个库的用户。通过模拟它的特性和输出来研究如何有效的使用它并捕获错误。

由于 onFullfilledonRejected 处理函数都可以由 .then 来处理,因此我们可以用下面这种写法来代替把 .then.catch 分开写的做法。

1
2
3
4
5
6
7
8
momsPromise.then(
function(value) {
console.log("Hurray I got this phone as a gift ", JSON.stringify(value));
},
function(reason) {
console.log("Mom coudn't buy me the phone because ", reason);
}
);

不过从可读性的角度来说,我还是觉得分开写比较好。

为了确保上面所有的例子都能在常见浏览器或 Chrome 中执行,我的代码中没有任何外部依赖。为了加深对主题的理解,我们来创建一个会返回 Promise 的函数,这个 Promise 会被随机的成功执行(resolved)或执行失败(rejected),这样我们就可以在不同的情境下测试我们的代码。为了理解异步函数这一概念,我们的代码中还将加入随机的延迟。既然用到了随机,我们先来创建一个随机返回在 X 和 Y 之间的数值的函数。

1
2
3
4
function getRandomNumber(start = 1, end = 10) {
//works when both start,end are >=1 and end > start
return parseInt(Math.random() * end) % (end-start+1) + start;
}

我们来创建一个返回 Promise 的函数。我们调用一下函数 promiseTRRARNOSG,它是函数 promiseThatResolvesRandomlyAfterRandomNumnberOfSecondsGenerator 的别名。这个函数会创建一个在 2 到 10 秒后被成功执行(resolved)或执行失败(rejected)的 Promise。为了使这个 Promise 的执行成功/失败随机化,我们会随机生成一个位于 1 到 10 之间的数字,如果数字大于 5,我们就让这个 Promise 成功执行(resolved),否则就让它执行失败(rejected)。

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
function getRandomNumber(start = 1, end = 10) {
//works when both start and end are >=1
return (parseInt(Math.random() * end) % (end - start + 1)) + start;
}

var promiseTRRARNOSG = (promiseThatResolvesRandomlyAfterRandomNumnberOfSecondsGenerator = function() {
return new Promise(function(resolve, reject) {
let randomNumberOfSeconds = getRandomNumber(2, 10);
setTimeout(function() {
let randomiseResolving = getRandomNumber(1, 10);
if (randomiseResolving > 5) {
resolve({
randomNumberOfSeconds: randomNumberOfSeconds,
randomiseResolving: randomiseResolving
});
} else {
reject({
randomNumberOfSeconds: randomNumberOfSeconds,
randomiseResolving: randomiseResolving
});
}
}, randomNumberOfSeconds * 1000);
});
});

var testProimse = promiseTRRARNOSG();
testProimse.then(function(value) {
console.log("Value when promise is resolved : ", value);
});
testProimse.catch(function(reason) {
console.log("Reason when promise is rejected : ", reason);
});

// Let us loop through and create ten different promises using the function to see some variation. Some will be resolved and some will be rejected.

for (i=1; i<=10; i++) {
let promise = promiseTRRARNOSG();
promise.then(function(value) {
console.log("Value when promise is resolved : ", value);
});
promise.catch(function(reason) {
console.log("Reason when promise is rejected : ", reason);
});
}

刷新浏览器并在 console 中执行上面的代码,以便查看 Promise 在 resolvereject 场景下的输出情况。接下来我们将看到如何在不写如此多的复杂代码的情况下创建多个 Promise 并检查它们的输出。

静态方法

Promise 对象有 4 个静态方法。

前两个是辅助方法,或者叫快捷方式。用它们可以快速创建被成功执行(resolved)或执行失败(rejected)状态的 Promise。

代码 Promise.reject(reason)

帮助你创建一个执行失败(rejected)状态的 Promise。

1
2
3
4
5
6
7
var promise3 = Promise.reject("Not interested");
promise3.then(function(value){
console.log("This will not run as it is a resolved promise. The resolved value is ", value);
});
promise3.catch(function(reason){
console.log("This run as it is a rejected promise. The reason is ", reason);
});

代码 Promise.resolve(value)

帮助你创建一个执行成功(resolved)状态的 Promise。

1
2
3
4
5
6
7
var promise4 = Promise.resolve(1);
promise4.then(function(value){
console.log("This will run as it is a resovled promise. The resolved value is ", value);
});
promise4.catch(function(reason){
console.log("This will not run as it is a resolved promise", reason);
});

顺带说一下,一个 Promise 可以有多个处理函数。所以上面的代码还可以这样写:

1
2
3
4
5
6
7
8
9
10
var promise4 = Promise.resolve(1);
promise4.then(function(value){
console.log("This will run as it is a resovled promise. The resolved value is ", value);
});
promise4.then(function(value){
console.log("This will also run as multiple handlers can be added. Printing twice the resolved value which is ", value * 2);
});
promise4.catch(function(reason){
console.log("This will not run as it is a resolved promise", reason);
});

输出结果为:

接下来的两个方法可以帮助你处理多个 Promise。当你要处理多个 Promise 时,最好是先创建一个 Promise 的数组,然后对这个数组采取相应的操作。这次我们不能用 promiseTRRARNOSG 这个函数做例子了,因为它太随机,对与理解我们要说的内容无益。在这里用可以预测其行为的 Promise 比较适合。我们创建两个函数,一个会在 n 秒后执行成功(resolve),另一个会在 n 秒后执行失败(reject)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var promiseTRSANSG = (promiseThatResolvesAfterNSecondsGenerator = function(
n = 0
) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve({
resolvedAfterNSeconds: n
});
}, n * 1000);
});
});
var promiseTRJANSG = (promiseThatRejectsAfterNSecondsGenerator = function(
n = 0
) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject({
rejectedAfterNSeconds: n
});
}, n * 1000);
});
});

接下来我们用这两个辅助函数来理解 Promise.All

Promise.All

据 MDN 文档

Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。

案例 1:所有的 Promise 都成功执行(resolved)。这也是最常见的场景。

1
2
3
4
5
6
7
8
9
10
11
12
13
console.time("Promise.All");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(1));
promisesArray.push(promiseTRSANSG(4));
promisesArray.push(promiseTRSANSG(2));
var handleAllPromises = Promise.all(promisesArray);
handleAllPromises.then(function(values) {
console.timeEnd("Promise.All");
console.log("All the promises are resolved", values);
});
handleAllPromises.catch(function(reason) {
console.log("One of the promises failed with the following reason", reason);
});

所有的 Promise 都成功执行

从输出结果中我们观察到两个非常重要的点。

第一点:第 3 个 Promise 花了 2 秒执行完毕,它比花了 4 秒钟才执行完毕的 第 2 个 Promise 要早。但是你从输出结果中看到,返回值的顺序与 Promise 在数组中的顺序是一致的。

第二点:我加了一个 console 计时器来计算 Promise.All 花费了多长时间。如果 Promise 们是依次执行的,那么花费的时间应该是 1+4+2=7 秒。然而从我们的计时器看到,它只花了 4 秒。这证明所有的 Promise 是平行执行的。

案例 2:没有任何 Promise。我想这种情况一般不会出现。

1
2
3
4
5
6
7
8
9
10
11
12
13
console.time("Promise.All");
var promisesArray = [];
promisesArray.push(1);
promisesArray.push(4);
promisesArray.push(2);
var handleAllPromises = Promise.all(promisesArray);
handleAllPromises.then(function(values) {
console.timeEnd("Promise.All");
console.log("All the promises are resolved", values);
});
handleAllPromises.catch(function(reason) {
console.log("One of the promises failed with the following reason", reason);
});

由于数组中没有 Promise,所以返回的 Promise 是执行成功(resolved)状态。

案例 3:失败信息是数组中第一个执行失败的 Promise 的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
console.time("Promise.All");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(1));
promisesArray.push(promiseTRSANSG(5));
promisesArray.push(promiseTRSANSG(3));
promisesArray.push(promiseTRJANSG(2));
promisesArray.push(promiseTRSANSG(4));
var handleAllPromises = Promise.all(promisesArray);
handleAllPromises.then(function(values) {
console.timeEnd("Promise.All");
console.log("All the promises are resolved", values);
});
handleAllPromises.catch(function(reason) {
console.timeEnd("Promise.All");
console.log("One of the promises failed with the following reason ", reason);
});

在遇到第一个执行失败的 Promise 后代码停止执行

Promise.race

据 MDN 文档:

Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

案例 1:某个 Promise 率先执行成功(resolved)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
console.time("Promise.race");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(4));
promisesArray.push(promiseTRSANSG(3));
promisesArray.push(promiseTRSANSG(2));
promisesArray.push(promiseTRJANSG(3));
promisesArray.push(promiseTRSANSG(4));
var promisesRace = Promise.race(promisesArray);
promisesRace.then(function(values) {
console.timeEnd("Promise.race");
console.log("The fasted promise resolved", values);
});
promisesRace.catch(function(reason) {
console.timeEnd("Promise.race");
console.log("The fastest promise rejected with the following reason ", reason);
});

输出的是执行最快的 Promise

所有的 Promise 都是平行执行的。第 3 个 Promise 用 2 秒执行成功(resolved),这个 Promise 执行完成后 Promise.race 返回的 Promise 就执行成功了(resolved)。

案例 2:某个 Promise 率先执行失败(rejected)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
console.time("Promise.race");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(4));
promisesArray.push(promiseTRSANSG(6));
promisesArray.push(promiseTRSANSG(5));
promisesArray.push(promiseTRJANSG(3));
promisesArray.push(promiseTRSANSG(4));
var promisesRace = Promise.race(promisesArray);
promisesRace.then(function(values) {
console.timeEnd("Promise.race");
console.log("The fasted promise resolved", values);
});
promisesRace.catch(function(reason) {
console.timeEnd("Promise.race");
console.log("The fastest promise rejected with the following reason ", reason);
});

输出的是执行最快的 Promise

所有的 Promise 都是平行执行的。第 4 个 Promise 用时 3 秒执行失败(rejected),在它执行完成后 Promise.race 返回的 Promise 就执行失败(rejected)了。

我写了这些例子以便我们可以在浏览器中执行并测试不同的场景。因此你可以看到我们没有使用任何外部的 API、文件操作或数据库调用,因为如果那样的话你就得做一些额外的操作来使示例代码运行了。我们是用延迟函数来模拟不同的场景,这不需要你做额外的设置。你还可以方便的修改相关数值来模拟不同的情况。通过结合使用 promiseTRJANSGpromiseTRSANSGpromiseTRRARNOSG 方法可以模拟足够多的场景,以便对 Promise 有更好的理解。同时通过在有关代码块的前后使用 console.time 方法可以方便的识别出 Promise 是平行执行还是顺序执行的。如果你还有某些特殊的场景我没有提到,请联系我。上面用到的所有代码都集合在这个 gist 里了:

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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
var keepsHisWord;
keepsHisWord = true;
promise1 = new Promise(function(resolve, reject) {
if (keepsHisWord) {
resolve("The man likes to keep his word");
} else {
reject("The man doesnt want to keep his word");
}
});
console.log(promise1);

promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve({
message: "The man likes to keep his word",
code: "aManKeepsHisWord"
});
}, 10 * 1000);
});
console.log(promise2);

keepsHisWord = false;
promise3 = new Promise(function(resolve, reject) {
if (keepsHisWord) {
resolve("The man likes to keep his word");
} else {
reject("The man doesn't want to keep his word");
}
});
console.log(promise3);

var momsPromise = new Promise(function(resolve, reject) {
momsSavings = 20000;
priceOfPhone = 60000;
if (momsSavings > priceOfPhone) {
resolve({
brand: "iphone",
model: "6s"
});
} else {
reject("We donot have enough savings. Let us save some more money.");
}
});
momsPromise.then(function(value) {
console.log("Hurray I got this phone as a gift ", JSON.stringify(value));
});
momsPromise.catch(function(reason) {
console.log("Mom coudn't buy me the phone because ", reason);
});
momsPromise.finally(function() {
console.log(
"Irrespecitve of whether my mom can buy me a phone or not, I still love her"
);
});

momsPromise.then(
function(value) {
console.log("Hurray I got this phone as a gift ", JSON.stringify(value));
},
function(reason) {
console.log("Mom coudn't buy me the phone because ", reason);
}
);

function getRandomNumber(start = 1, end = 10) {
//works when both start,end are >=1 and end > start
return parseInt(Math.random() * end) % (end-start+1) + start;
}

function getRandomNumber(start = 1, end = 10) {
//works when both start and end are >=1
return (parseInt(Math.random() * end) % (end - start + 1)) + start;
}
var promiseTRRARNOSG = (promiseThatResolvesRandomlyAfterRandomNumnberOfSecondsGenerator = function() {
return new Promise(function(resolve, reject) {
let randomNumberOfSeconds = getRandomNumber(2, 10);
setTimeout(function() {
let randomiseResolving = getRandomNumber(1, 10);
if (randomiseResolving > 5) {
resolve({
randomNumberOfSeconds: randomNumberOfSeconds,
randomiseResolving: randomiseResolving
});
} else {
reject({
randomNumberOfSeconds: randomNumberOfSeconds,
randomiseResolving: randomiseResolving
});
}
}, randomNumberOfSeconds * 1000);
});
});
var testProimse = promiseTRRARNOSG();
testProimse.then(function(value) {
console.log("Value when promise is resolved : ", value);
});
testProimse.catch(function(reason) {
console.log("Reason when promise is rejected : ", reason);
});
// Let us loop through and create ten different promises using the function to see some variation. Some will be resolved and some will be rejected.
for (i=1; i<=10; i++) {
let promise = promiseTRRARNOSG();
promise.then(function(value) {
console.log("Value when promise is resolved : ", value);
});
promise.catch(function(reason) {
console.log("Reason when promise is rejected : ", reason);
});
}

var promise3 = Promise.reject("Not interested");
promise3.then(function(value){
console.log("This will not run as it is a resolved promise. The resolved value is ", value);
});
promise3.catch(function(reason){
console.log("This run as it is a rejected promise. The reason is ", reason);
});

var promise4 = Promise.resolve(1);
promise4.then(function(value){
console.log("This will run as it is a resovled promise. The resolved value is ", value);
});
promise4.catch(function(reason){
console.log("This will not run as it is a resolved promise", reason);
});


var promise4 = Promise.resolve(1);
promise4.then(function(value){
console.log("This will run as it is a resovled promise. The resolved value is ", value);
});
promise4.then(function(value){
console.log("This will also run as multiple handlers can be added. Printing twice the resolved value which is ", value * 2);
});
promise4.catch(function(reason){
console.log("This will not run as it is a resolved promise", reason);
});


var promiseTRSANSG = (promiseThatResolvesAfterNSecondsGenerator = function(
n = 0
) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve({
resolvedAfterNSeconds: n
});
}, n * 1000);
});
});
var promiseTRJANSG = (promiseThatRejectsAfterNSecondsGenerator = function(
n = 0
) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject({
rejectedAfterNSeconds: n
});
}, n * 1000);
});
});

console.time("Promise.All");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(1));
promisesArray.push(promiseTRSANSG(4));
promisesArray.push(promiseTRSANSG(2));
var handleAllPromises = Promise.all(promisesArray);
handleAllPromises.then(function(values) {
console.timeEnd("Promise.All");
console.log("All the promises are resolved", values);
});
handleAllPromises.catch(function(reason) {
console.log("One of the promises failed with the following reason", reason);
});

console.time("Promise.All");
var promisesArray = [];
promisesArray.push(1);
promisesArray.push(4);
promisesArray.push(2);
var handleAllPromises = Promise.all(promisesArray);
handleAllPromises.then(function(values) {
console.timeEnd("Promise.All");
console.log("All the promises are resolved", values);
});
handleAllPromises.catch(function(reason) {
console.log("One of the promises failed with the following reason", reason);
});


console.time("Promise.All");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(1));
promisesArray.push(promiseTRSANSG(5));
promisesArray.push(promiseTRSANSG(3));
promisesArray.push(promiseTRJANSG(2));
promisesArray.push(promiseTRSANSG(4));
var handleAllPromises = Promise.all(promisesArray);
handleAllPromises.then(function(values) {
console.timeEnd("Promise.All");
console.log("All the promises are resolved", values);
});
handleAllPromises.catch(function(reason) {
console.timeEnd("Promise.All");
console.log("One of the promises failed with the following reason ", reason);
});


console.time("Promise.race");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(4));
promisesArray.push(promiseTRSANSG(3));
promisesArray.push(promiseTRSANSG(2));
promisesArray.push(promiseTRJANSG(3));
promisesArray.push(promiseTRSANSG(4));
var promisesRace = Promise.race(promisesArray);
promisesRace.then(function(values) {
console.timeEnd("Promise.race");
console.log("The fasted promise resolved", values);
});
promisesRace.catch(function(reason) {
console.timeEnd("Promise.race");
console.log("The fastest promise rejected with the following reason ", reason);
});


console.time("Promise.race");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(4));
promisesArray.push(promiseTRSANSG(6));
promisesArray.push(promiseTRSANSG(5));
promisesArray.push(promiseTRJANSG(3));
promisesArray.push(promiseTRSANSG(4));
var promisesRace = Promise.race(promisesArray);
promisesRace.then(function(values) {
console.timeEnd("Promise.race");
console.log("The fasted promise resolved", values);
});
promisesRace.catch(function(reason) {
console.timeEnd("Promise.race");
console.log("The fastest promise rejected with the following reason ", reason);
});

Bluebird 库还有一些有趣的功能比如:

  1. Promise.prototype.timeout
  2. Promise.some
  3. Promise.promisify

我们会在单独的文章中讨论。

我还会写几篇关于我学习 async 和 await 的文章。

在结束前我想列一下我学习 Promise 期间帮助我保持头脑清醒的要点。

使用 Promise 的要点

  1. 当你想用异步或阻塞型代码时请使用 Promise。
  2. 在实践中将 resoleve 对应 thenreject 对应 catch
  3. 确保为所有的 Promise 都写上 .catch.then
  4. 如果某个操作无论如何都要执行请使用 .finally
  5. 每个 Promise 只能执行一次。
  6. 我们可以为一个 Promise 添加多个处理函数。
  7. Promise 对象的方法返回的数据类型都是 Promise,无论时静态方法还是原型方法。
  8. Promise.all 中,不管是哪个 Promise 先执行完成,返回值的顺序与输入时一致。

如果我遗漏了任何内容,或是有任何内容需要修正请指出。由于我们正在 MERN stack 上开发我们的产品 https://learningpaths.io/,我开始花费更多的时间在 JavaScript 上。我花了几天的时间来阅读 Promise 相关内容,记了相当多的链接和笔记。因此我们开始开发一个 Chrome 扩展来解决这一痛点,这个扩展可以帮你管理你的学习路线。目前此扩展还在 alpha 阶段,我们想选择 100 个愿意尝试并提供反馈的用户来试用它。如果你感兴趣可以去 https://learningpaths.io/ 申请早期试用。如果你觉得你的朋友或同事可能感兴趣请分享给他们。

如果你喜欢这篇文章并想阅读其他类似文章,别忘了点赞哟。

「译」7 个用 Async/Await 取代 Promise 的理由(教程) 「译」深度理解 JavaScript ES6 中的 “Super” 和 “Extends”
广告: