Skip to content

Commit 4b24c7e

Browse files
committed
完成初稿
1 parent aec02c9 commit 4b24c7e

8 files changed

+169
-55
lines changed

02-04-promise-advanced.md

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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
1943
Promise.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
149173
let 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
179205
new 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
210236
new 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 => {
246274
function 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) {
276304
queue(['lots', 'of', 'things', ....]);
277305
```
278306

279-
这个例子如此直接我就不再详细解释了。下面我们看一个相对复杂的例子,
307+
这个例子如此直接我就不再详细解释了。下面我们看一个相对复杂的例子,假设需求:
280308

281-
> 假设需求:开发一个爬虫,抓取某网站。
309+
> 开发一个爬虫,抓取某网站。
282310
283311
```javascript
312+
const spider = require('spider');
313+
284314
function fetchAll(urls) {
285315
return urls.reduce((promise, url) => {
286316
return promise.then( () => {
@@ -302,7 +332,7 @@ let url = ['http://blog.meathill.com/'];
302332
fetchAll(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'];
346376
fetch(urls);
347377
```
348378

349-
Generator 可以把所有待抓取的 URL 都放到一个数组里,然后慢慢加载。从整体来看,暴露给外界的 `fetch` 函数其实变简单了很多。但是实现 Generator 本身有点费工夫,其中的利弊大家自己权衡吧。
379+
Generator 可以把所有待抓取的 URL 都放到一个数组里,然后慢慢加载。从整体来看,暴露给外界的 `fetch` 函数其实变简单了很多。但是实现 Generator 本身有点费工夫,其中的利弊大家自己权衡吧。
380+
381+
## 小结
382+
383+
关于 Promise 的内容到此告一段落。这里我介绍了大部分的功能、函数和常见用法,有一些特殊情况会在后面继续说明。

02-2-promise-test.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
Promise 小测验
22
========
33

4-
好的,我们已经初步了解了 Promise 的使用方式了解了 `.then()` 怎么处理响应函数,返回新的 Promise 实例。接下来我们看一个小测验,看看大家的掌握程度。
4+
好的,我们已经初步了解了 Promise 的使用方式了解了 `.then()` 怎么处理响应函数;知道 `.then()` 会返回新的 Promise 实例,所以可以链式调用;还知道 `Promise.resolve()` 是怎么转化普通类型到 Promise 类型的。可能有些同学看到这里,自我感觉良好,觉得已经学完了。所以接下来我们来做一个小测验,看看大家的掌握程度。
55

66
> 下面这道题出自 [We have a problem with promises](https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html)
77
88
**问题:下面的四种 promises 的区别是什么?**
99

10-
我们假定 `doSomething()``doSomethingElse()` 都会返回 Promise 对象。
10+
(假定 `doSomething()``doSomethingElse()` 都会返回 Promise 对象。另外请尽量在足够宽的屏幕上观看,不然换行可能会影响视觉效果。)
1111

1212
```javascript
1313
// #1
@@ -83,7 +83,7 @@ doSomething
8383
|------------------|
8484
```
8585

86-
这道题就有一定难度了。虽然 `doSomethingElse` 会返回 Promise 对象,但是因为 `.then()` 的响应函数并没有把它 `return` 出来,所以这里其实相当于 `return null`。我们知道,`Promise.resolve()` 在参数为空的时候会返回一个状态为 `fulfilled` 的 Promise,所以这里两步是一起执行的
86+
这道题就有一定难度了。虽然 `doSomethingElse` 会返回 Promise 对象,但是因为 `.then()` 的响应函数并没有把它 `return` 出来,所以这里其实相当于 `return null`。我们知道,`Promise.resolve()` 在参数为空的时候会返回一个状态为 `fulfilled` 的 Promise,所以这里两步是几乎一起执行的
8787

8888
### 第三题
8989

@@ -104,7 +104,7 @@ doSomethingElse(undefined)
104104
|------------------|
105105
```
106106

107-
这一题的语法陷阱也不小。首先,`doSomethingElse``doSomethingElse()` 的区别在于,前者是一个变量,引用一个函数;而后者是则是直接执行了函数,并返回其返回值。所以这里 `doSomethingElse` 立刻就开始执行了,和前面 `doSomething` 的启动时间相差无几,可以忽略不计。然后,按照 Promise 的设计,当 `.then()` 的参数不是函数的时候,这一步会被忽略不计,所以 `doSomething` 完成后就跳去执行 `finalHandler` 了。
107+
这一题的语法陷阱也不小。首先,`doSomethingElse``doSomethingElse()` 的区别在于,前者是变量,指向一个函数;而后者是则是直接执行了函数,并返回其返回值。所以这里 `doSomethingElse` 立刻就开始执行了,和前面 `doSomething` 的启动时间相差无几,可以忽略不计。然后,按照 Promise 的设计,当 `.then()` 的参数不是函数的时候,这一步会被忽略不计,所以 `doSomething` 完成后就跳去执行 `finalHandler` 了。
108108

109109
### 第四题
110110

02-3-promise-error.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,11 @@ new Promise(resolve => {
7575
7676
实际上,`.catch()` 仍然会使用 `Promise.resolve()` 返回其中的响应函数的执行结果,与 `.then()` 并无不同。所以 `.catch()` 之后的 `.then()` 仍然会执行,如果想彻底跳出执行,就必须继续抛出错误,比如把上面代码中的 `another error` 那行注释掉。这也需要大家注意。
7777
78-
## 总结
78+
所以,我们也可以下结论,Promise 并没有真正请回 `try/catch/throw`,它只是模拟了一个 `.catch()` 出来,可以在一定程度上解决回调错误的问题,但是距离真正还原栈关系,正常使用 `try/catch/throw` 其实还很远。
7979
80-
简单总结一下 Promise 的错误处理。与异步回调相比,它的作用略强,可以抛出和捕获,基本可以按照预期的状态执行。然而它仍然不是真正的 `try/catch/throw`,在队列很长的时候,捕获错误也很容易出错,所以还要小心。
80+
## 小结
81+
82+
简单总结一下 Promise 的错误处理。与异步回调相比,它的作用略强,可以抛出和捕获错误,基本可以按照预期的状态执行。然而它仍然不是真正的 `try/catch/throw``.catch()` 也使用 `Promise.resolve()` 返回 `fulfilled` 状态的 Promise 实例,所以它后面的 `.then()` 会继续执行,在队列很长的时候,也容易出错,请大家务必小心。
8183
8284
另外,所有执行器和响应函数里的错误都不会真正进入全局环境,所以我们有必要在所有队列的最后一步增加一个 `.catch()`,防止遗漏错误造成意想不到的问题。
8385
@@ -90,4 +92,4 @@ doSomething()
9092
});
9193
```
9294
93-
在 Node.js7 之后,没有捕获的 Promise 错误会触发一个 Warning,虽然不是很强,但也足够大家发现错误了
95+
在 Node.js v7 之后,没有捕获的内部错误会触发一个 Warning,大家可以用来发现错误

03-1-async-function-vs-promise.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ Async Function VS Promise
33

44
## 异步函数的优势
55

6-
异步函数对异步编程来说,有非常很大的提升。尤其在 `try/catch/throw` 方面,异步函数可以延续以前的标准写法,优势非常大。
6+
异步函数对异步编程来说,有非常很大的提升。尤其在 `try/catch/throw` 方面,异步函数可以延续以前的标准写法,还能正常检索堆栈信息,优势非常大。
77

8-
## Promise 的优势
8+
## Promise 的价值
99

10-
不过目前来看,Promise (暂时)并不会被完全取代。
10+
不过就目前来看,Promise (暂时)并不会被完全取代。
1111

1212
### 异步函数依赖 Promise
1313

@@ -17,16 +17,16 @@ Async Function VS Promise
1717

1818
### Promise 能更好的提供队列操作,并且在对象之间传递
1919

20-
Promise 本身是一个对象,所以可以任意传递。这意味着我们可以在程序的任何位置使用和维护队列,这会比其它方式都方便
20+
Promise 本身是一个对象,所以可以在代码中任意传递。这意味着我们可以在程序的任何位置使用和维护队列,非常方便
2121

22-
比如我们做一些办公产品,用户可能先做了 A,然后又做了 B。然而 A 和 B 之间需要很强的时间关联,顺序不能乱。这个时候,队列就比两次异步请求更合适
22+
比如我们做一些办公产品,用户可能先做了 A,然后又做了 B。A 和 B 之间存在很强的先后顺序,不能乱。这个时候,队列的价值就能充分提现出来
2323

24-
### 异步函数的覆盖率还不够
24+
### 异步函数的支持率还不够
2525

2626
从上一节最后的截图可以看到,虽然异步函数的支持率已经很高了,但是在 iOS 10.2、Android 4 等关键平台上,还没有原生实现。这就需要我们进行降级兼容。
2727

28-
然而异步函数的降级代码很难写,Babel 转译后至少要增加3000行,对于我们的应用来说是一个不小的代价。如果应用本身不大,或者异步操作并不复杂,可能用 Promise 是个更好的选择
28+
然而异步函数的降级代码很难写,Babel 转译后至少要增加3000行,对于一些轻量级应用来说代价不小。如果应用本身不大,或者异步操作并不复杂, Promise 可能是更好的选择
2929

30-
## 总结
30+
## 小结
3131

32-
根据需求和场景选择合适的技术,永远不会错。
32+
Promise 和异步函数之间不存在替代关系,根据需求和场景选择合适的技术,永远不会错。

0 commit comments

Comments
 (0)