@@ -7,13 +7,37 @@ Promise 进阶
7
7
8
8
这个方法比较简单,就返回一个状态为 ` rejected ` 的 Promise 实例。
9
9
10
- 它接受一个参数,为了方便后续操作,最好创建一个 Error 实例 。
10
+ 它接受一个参数 ` reason ` ,作为状态说明,交由后面的 ` .catch() ` 捕获。为了与其它异常处理共用一个 ` .catch() ` ,我们可以用 ` Error ` 实例作为 ` reason ` 。
11
11
12
- ## ` Promise.all() `
12
+ 另外,` Promise.reject() ` 也不认 ` thenable ` 。
13
+
14
+ ``` javascript
15
+ let error = new Error (' something wrong' );
16
+ Promise .reject (error)
17
+ .then ( value => {
18
+ console .log (' it\' s ok' );
19
+ console .log (value);
20
+ })
21
+ .catch ( err => {
22
+ console .log (' no, it\' s not ok' );
23
+ console .log (err);
24
+
25
+ return Promise .reject ({
26
+ then () {
27
+ console .log (' it will be ok' );
28
+ },
29
+ catch () {
30
+ console .log (' not yet' );
31
+ }
32
+ });
33
+ });
34
+ ```
35
+
36
+ ## ` Promise.all([p1, p2, p3, ....]) `
13
37
14
38
` Promise.all([p1, p2, p3, ....]) ` 用于将多个 Promise 实例,包装成一个新的 Promise 实例。
15
39
16
- 它接受一个数组作为参数 ,数组里可以是 Promise 对象,也可以是别的值,这些值都会交给 ` Promise.resolve() ` 处理。当所有子 Promise 都完成,该 Promise 完成,返回值是包含全部返回值的数组。有任何一个失败,该 Promise 失败,` .catch() ` 得到的是第一个失败的子 Promise 的错误。
40
+ 它接受一个数组(其实是 [ ` iterable ` ] ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterable_protocol ) ,不过我觉得暂时不要引入更多概念了……)作为参数 ,数组里可以是 Promise 对象,也可以是别的值,这些值都会交给 ` Promise.resolve() ` 处理。当所有子 Promise 都完成,该 Promise 完成,返回值是包含全部返回值的数组。有任何一个失败,该 Promise 失败,` .catch() ` 得到的是第一个失败的子 Promise 的错误。
17
41
18
42
``` javascript
19
43
Promise .all ([1 , 2 , 3 ])
@@ -137,13 +161,13 @@ findLargest('some/path/')
137
161
});
138
162
```
139
163
140
- 在这个例子当中,我使用 Promise 将 ` fs.stat ` 和 ` fs.readdir ` 进行了封装,让其返回 Promise 对象。然后使用 ` Promise.all() ` + ` array. map()` 方法,就可以对其进行遍历 ,还可以避免使用外层作用域的变量。
164
+ 在这个例子当中,我使用 Promise 将 ` fs.stat ` 和 ` fs.readdir ` 进行了封装,让其返回 Promise 对象。然后使用 ` Promise.all() ` + ` Array.prototype. map()` 方法,就可以进行遍历 ,还可以避免使用外层作用域的变量。
141
165
142
- ## ` Promise.race() `
166
+ ## ` Promise.race([p1, p2, p3, ....] ) `
143
167
144
168
` Promise.race() ` 的功能和用法与 ` Promise.all() ` 十分类似,也接受一个数组作为参数,然后把数组里的值都用 ` Promise.resolve() ` 处理成 Promise 对象,然后再返回一个新的 Promise 实例。只不过这些子 Promise 有任意一个完成,` Promise.race() ` 返回的 Promise 实例就算完成,并且返回完成的子实例的返回值。
145
169
146
- 它最常见的用法,是作为超时检查 。我们可以把异步操作和定时器放在一个 ` Promise.race() ` 里,如果定时器触发的时候异步操作还没返回 ,就可以认为超时了。
170
+ 它最常见的用法,是作超时检查 。我们可以把异步操作和定时器放在一个 ` Promise.race() ` 里,如果定时器触发时异步操作还没返回 ,就可以认为超时了,然后就可以给用户一些提示 。
147
171
148
172
``` javascript
149
173
let p1 = new Promise (resolve => {
@@ -171,9 +195,11 @@ Promise.race([p1, p2])
171
195
// Timeout, Yellow flower is cold
172
196
```
173
197
198
+ 注意,这里 ` p1 ` 也就是原本就要执行的异步操作并没有被中止,它只是没有在预期的时间内返回而已。所以一方面可以继续等待它的返回值,另一方面也要考虑服务器端是否需要做回滚处理。
199
+
174
200
## Promise 嵌套
175
201
176
- 这种情况在初用 Promise 的同学的代码中很常见,大概是这么个意思:
202
+ 这种情况在初涉 Promise 的同学的代码中很常见,大概是这么个意思:
177
203
178
204
``` javascript
179
205
new Promise ( resolve => {
@@ -204,7 +230,7 @@ new Promise( resolve => {
204
230
});
205
231
```
206
232
207
- 因为 ` .then() ` 返回的也是 Promise 实例,所以外层的 Promise 会等里面的 ` .then() ` 执行完再继续执行,所以这里的执行顺序稳定为 “1 > 1-1 > 1-2 > 1-3 > 2”。但是从阅读体验和维护效率的角度来看,最好把它展开:
233
+ 因为 ` .then() ` 返回的也是 Promise 实例,所以外层的 Promise 会等里面的 ` .then() ` 执行完再继续执行,所以这里的执行顺序稳定为从上之下,左右无关, “1 > 1-1 > 1-2 > 1-3 > 2”。但是从阅读体验和维护效率的角度来看,最好把它展开:
208
234
209
235
``` javascript
210
236
new Promise ( resolve => {
@@ -235,9 +261,11 @@ new Promise( resolve => {
235
261
});
236
262
```
237
263
264
+ 二者是完全等价的,后者更容易阅读。
265
+
238
266
## 队列
239
267
240
- 有时候我们不希望所有动作一起发生,而是按照一定顺序,逐个进行。这样的形式,就是队列。在我看来,队列是 Promise 的核心用法。即使在异步函数实装的今天,队列也有其独特的价值 。
268
+ 有时候我们不希望所有动作一起发生,而是按照一定顺序,逐个进行。这样的形式,就是队列。在我看来,队列是 Promise 的核心价值,即使是异步函数在大部分浏览器和 Node.js 里实装的今天,队列也仍有其独特的价值 。
241
269
242
270
用 Promise 实现队列的方式很多,这里兹举两例:
243
271
@@ -246,7 +274,7 @@ new Promise( resolve => {
246
274
function queue (things ) {
247
275
let promise = Promise .resolve ();
248
276
things .forEach ( thing => {
249
- // 这里很重要 ,如果不把 `.then()` 返回的新实例赋给 `promise` 的话,就不是队列,而是批量执行
277
+ // 这里很容易出错 ,如果不把 `.then()` 返回的新实例赋给 `promise` 的话,就不是队列,而是批量执行
250
278
promise = promise .then ( () => {
251
279
return new Promise ( resolve => {
252
280
doThing (thing, () => {
@@ -276,11 +304,13 @@ function queue(things) {
276
304
queue ([' lots' , ' of' , ' things' , ... .]);
277
305
```
278
306
279
- 这个例子如此直接我就不再详细解释了。下面我们看一个相对复杂的例子,
307
+ 这个例子如此直接我就不再详细解释了。下面我们看一个相对复杂的例子,假设需求:
280
308
281
- > 假设需求: 开发一个爬虫,抓取某网站。
309
+ > 开发一个爬虫,抓取某网站。
282
310
283
311
``` javascript
312
+ const spider = require (' spider' );
313
+
284
314
function fetchAll (urls ) {
285
315
return urls .reduce ((promise , url ) => {
286
316
return promise .then ( () => {
@@ -302,7 +332,7 @@ let url = ['http://blog.meathill.com/'];
302
332
fetchAll (url);
303
333
```
304
334
305
- 这段代码,我假设有一个蜘蛛工具(spider)包含基本的抓取和分析功能,然后循环使用 ` fetch ` 和 ` fetchAll ` 方法,不断分析抓取的页面,然后把页面当中所有的链接都加入到抓取队列当中。
335
+ 这段代码,我假设有一个蜘蛛工具(spider)包含基本的抓取和分析功能,然后循环使用 ` fetch ` 和 ` fetchAll ` 方法,不断分析抓取的页面,然后把页面当中所有的链接都加入到抓取队列当中。通过递归循环的方式,完成网站抓取。
306
336
307
337
### Generator
308
338
@@ -346,4 +376,8 @@ let urls = ['http://blog.meathill.com'];
346
376
fetch (urls);
347
377
```
348
378
349
- Generator 可以把所有待抓取的 URL 都放到一个数组里,然后慢慢加载。从整体来看,暴露给外界的 ` fetch ` 函数其实变简单了很多。但是实现 Generator 本身有点费工夫,其中的利弊大家自己权衡吧。
379
+ Generator 可以把所有待抓取的 URL 都放到一个数组里,然后慢慢加载。从整体来看,暴露给外界的 ` fetch ` 函数其实变简单了很多。但是实现 Generator 本身有点费工夫,其中的利弊大家自己权衡吧。
380
+
381
+ ## 小结
382
+
383
+ 关于 Promise 的内容到此告一段落。这里我介绍了大部分的功能、函数和常见用法,有一些特殊情况会在后面继续说明。
0 commit comments