@@ -7,13 +7,37 @@ Promise 进阶
77
88这个方法比较简单,就返回一个状态为 ` rejected ` 的 Promise 实例。
99
10- 它接受一个参数,为了方便后续操作,最好创建一个 Error 实例 。
10+ 它接受一个参数 ` reason ` ,作为状态说明,交由后面的 ` .catch() ` 捕获。为了与其它异常处理共用一个 ` .catch() ` ,我们可以用 ` Error ` 实例作为 ` reason ` 。
1111
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, ....]) `
1337
1438` Promise.all([p1, p2, p3, ....]) ` 用于将多个 Promise 实例,包装成一个新的 Promise 实例。
1539
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 的错误。
1741
1842``` javascript
1943Promise .all ([1 , 2 , 3 ])
@@ -137,13 +161,13 @@ findLargest('some/path/')
137161 });
138162```
139163
140- 在这个例子当中,我使用 Promise 将 ` fs.stat ` 和 ` fs.readdir ` 进行了封装,让其返回 Promise 对象。然后使用 ` Promise.all() ` + ` array. map()` 方法,就可以对其进行遍历 ,还可以避免使用外层作用域的变量。
164+ 在这个例子当中,我使用 Promise 将 ` fs.stat ` 和 ` fs.readdir ` 进行了封装,让其返回 Promise 对象。然后使用 ` Promise.all() ` + ` Array.prototype. map()` 方法,就可以进行遍历 ,还可以避免使用外层作用域的变量。
141165
142- ## ` Promise.race() `
166+ ## ` Promise.race([p1, p2, p3, ....] ) `
143167
144168` Promise.race() ` 的功能和用法与 ` Promise.all() ` 十分类似,也接受一个数组作为参数,然后把数组里的值都用 ` Promise.resolve() ` 处理成 Promise 对象,然后再返回一个新的 Promise 实例。只不过这些子 Promise 有任意一个完成,` Promise.race() ` 返回的 Promise 实例就算完成,并且返回完成的子实例的返回值。
145169
146- 它最常见的用法,是作为超时检查 。我们可以把异步操作和定时器放在一个 ` Promise.race() ` 里,如果定时器触发的时候异步操作还没返回 ,就可以认为超时了。
170+ 它最常见的用法,是作超时检查 。我们可以把异步操作和定时器放在一个 ` Promise.race() ` 里,如果定时器触发时异步操作还没返回 ,就可以认为超时了,然后就可以给用户一些提示 。
147171
148172``` javascript
149173let p1 = new Promise (resolve => {
@@ -171,9 +195,11 @@ Promise.race([p1, p2])
171195// Timeout, Yellow flower is cold
172196```
173197
198+ 注意,这里 ` p1 ` 也就是原本就要执行的异步操作并没有被中止,它只是没有在预期的时间内返回而已。所以一方面可以继续等待它的返回值,另一方面也要考虑服务器端是否需要做回滚处理。
199+
174200## Promise 嵌套
175201
176- 这种情况在初用 Promise 的同学的代码中很常见,大概是这么个意思:
202+ 这种情况在初涉 Promise 的同学的代码中很常见,大概是这么个意思:
177203
178204``` javascript
179205new Promise ( resolve => {
@@ -204,7 +230,7 @@ new Promise( resolve => {
204230 });
205231```
206232
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”。但是从阅读体验和维护效率的角度来看,最好把它展开:
208234
209235``` javascript
210236new Promise ( resolve => {
@@ -235,9 +261,11 @@ new Promise( resolve => {
235261 });
236262```
237263
264+ 二者是完全等价的,后者更容易阅读。
265+
238266## 队列
239267
240- 有时候我们不希望所有动作一起发生,而是按照一定顺序,逐个进行。这样的形式,就是队列。在我看来,队列是 Promise 的核心用法。即使在异步函数实装的今天,队列也有其独特的价值 。
268+ 有时候我们不希望所有动作一起发生,而是按照一定顺序,逐个进行。这样的形式,就是队列。在我看来,队列是 Promise 的核心价值,即使是异步函数在大部分浏览器和 Node.js 里实装的今天,队列也仍有其独特的价值 。
241269
242270用 Promise 实现队列的方式很多,这里兹举两例:
243271
@@ -246,7 +274,7 @@ new Promise( resolve => {
246274function queue (things ) {
247275 let promise = Promise .resolve ();
248276 things .forEach ( thing => {
249- // 这里很重要 ,如果不把 `.then()` 返回的新实例赋给 `promise` 的话,就不是队列,而是批量执行
277+ // 这里很容易出错 ,如果不把 `.then()` 返回的新实例赋给 `promise` 的话,就不是队列,而是批量执行
250278 promise = promise .then ( () => {
251279 return new Promise ( resolve => {
252280 doThing (thing, () => {
@@ -276,11 +304,13 @@ function queue(things) {
276304queue ([' lots' , ' of' , ' things' , ... .]);
277305```
278306
279- 这个例子如此直接我就不再详细解释了。下面我们看一个相对复杂的例子,
307+ 这个例子如此直接我就不再详细解释了。下面我们看一个相对复杂的例子,假设需求:
280308
281- > 假设需求: 开发一个爬虫,抓取某网站。
309+ > 开发一个爬虫,抓取某网站。
282310
283311``` javascript
312+ const spider = require (' spider' );
313+
284314function fetchAll (urls ) {
285315 return urls .reduce ((promise , url ) => {
286316 return promise .then ( () => {
@@ -302,7 +332,7 @@ let url = ['http://blog.meathill.com/'];
302332fetchAll (url);
303333```
304334
305- 这段代码,我假设有一个蜘蛛工具(spider)包含基本的抓取和分析功能,然后循环使用 ` fetch ` 和 ` fetchAll ` 方法,不断分析抓取的页面,然后把页面当中所有的链接都加入到抓取队列当中。
335+ 这段代码,我假设有一个蜘蛛工具(spider)包含基本的抓取和分析功能,然后循环使用 ` fetch ` 和 ` fetchAll ` 方法,不断分析抓取的页面,然后把页面当中所有的链接都加入到抓取队列当中。通过递归循环的方式,完成网站抓取。
306336
307337### Generator
308338
@@ -346,4 +376,8 @@ let urls = ['http://blog.meathill.com'];
346376fetch (urls);
347377```
348378
349- Generator 可以把所有待抓取的 URL 都放到一个数组里,然后慢慢加载。从整体来看,暴露给外界的 ` fetch ` 函数其实变简单了很多。但是实现 Generator 本身有点费工夫,其中的利弊大家自己权衡吧。
379+ Generator 可以把所有待抓取的 URL 都放到一个数组里,然后慢慢加载。从整体来看,暴露给外界的 ` fetch ` 函数其实变简单了很多。但是实现 Generator 本身有点费工夫,其中的利弊大家自己权衡吧。
380+
381+ ## 小结
382+
383+ 关于 Promise 的内容到此告一段落。这里我介绍了大部分的功能、函数和常见用法,有一些特殊情况会在后面继续说明。
0 commit comments