Skip to content

Commit 8f50096

Browse files
committed
update
1 parent 40d4d50 commit 8f50096

8 files changed

+444
-5
lines changed

README.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@
2424

2525
**part3 Promise**
2626

27-
- Promise ES6 采用
28-
- Promise 在 ES6 中的具体应用
29-
- 对标一下 Promise/A+ 标准
30-
- Promise 真的取代 callback 了吗?
31-
- 用 Q.js 库来简化 Promise 并且兼容浏览器
27+
- [Promise 加入 ES6 标准](./part3-promise/01-promise-in-es6.md)
28+
- [Promise 在 ES6 中的具体应用](./part3-promise/02-promise-use.md)
29+
- [对标一下 Promise/A+ 规范](./part3-promise/03-promise-standard.md)
30+
- [Promise 真的取代 callback 了吗?](./part3-promise/04-promise-callback.md)
31+
- [用 Q.js ](./part3-promise/05-promise-q.md)
3232

3333
**part4 Generator**
3434

part2-jquery/03-jquery-promise.md

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
上一节通过一些代码演示,知道了 jquery 的`deferred`对象是解决了异步中`callback`函数的问题,但是
44

5+
本节使用的代码参见[这里](./test.html)
6+
57
## 本节内容概述
68

79
- 返回`promise`

part3-promise/01-promise-in-es6.md

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
## Promise 加入 ES6 标准
2+
3+
从 jquery v1.5 发布经过若干时间之后,Promise 终于出现在了 ES6 的标准中,而当下 ES6 也正在被大规模使用。
4+
5+
本节展示的代码参考[这里](./test.js)
6+
7+
## 本节内容概述
8+
9+
- 写一段传统的异步操作
10+
-`Promise`进行封装
11+
12+
## 写一段传统的异步操作
13+
14+
还是拿之前讲 jquery `deferred`对象时的那段`setTimeout`程序
15+
16+
```javascript
17+
var wait = function () {
18+
var task = function () {
19+
console.log('执行完成')
20+
}
21+
setTimeout(task, 2000)
22+
}
23+
wait()
24+
```
25+
26+
之前我们使用 jquery 封装的,接下来将使用 ES6 的`Promise`进行封装,大家注意看有何不同。
27+
28+
## `Promise`进行封装
29+
30+
```javascript
31+
const wait = function () {
32+
// 定义一个 promise 对象
33+
const promise = new Promise((resolve, reject) => {
34+
// 将之前的异步操作,包括到这个 new Promise 函数之内
35+
const task = function () {
36+
console.log('执行完成')
37+
resolve() // callback 中去执行 resolve 或者 reject
38+
}
39+
setTimeout(task, 2000)
40+
})
41+
// 返回 promise 对象
42+
return promise
43+
}
44+
```
45+
46+
注意看看程序中的注释,那都是重点部分。从整体看来,感觉这次比用 jquery 那次简单一些,逻辑上也更加清晰一些。
47+
48+
- 将之前的异步操作那几行程序,用`new Promise((resolve,reject) => {.....})`包装起来,最后`return`即可
49+
- 异步操作的内部,在`callback`中执行`resolve()`(表明成功了,失败的话执行`reject`
50+
51+
接着上面的程序继续往下写。`wait()`返回的肯定是一个`promise`对象,而`promise`对象有`then`属性。
52+
53+
```javascript
54+
const w = wait()
55+
w.then(() => {
56+
console.log('ok 1')
57+
}, () => {
58+
console.log('err 1')
59+
}).then(() => {
60+
console.log('ok 2')
61+
}, () => {
62+
console.log('err 2')
63+
})
64+
```
65+
66+
`then`还是和之前一样,接收两个参数(函数),第一个在成功时(触发`resolve`)执行,第二个在失败时(触发`reject`)时执行。而且,`then`还可以进行链式操作。
67+
68+
以上就是 ES6 的`Promise`的基本使用演示。看完你可能会觉得,这跟之前讲述 jquery 的不差不多吗 ———— 对了,这就是我要在之前先讲 jquery 的原因,让你感觉一篇一篇看起来如丝般顺滑!
69+
70+
接下来,将详细说一下 ES6 `Promise` 的一些比较常见的用法,敬请期待吧!
71+
72+
## 求打赏
73+
74+
如果你看完了,感觉还不错,欢迎给我打赏 ———— 以激励我更多输出优质内容
75+
76+
![](http://images2015.cnblogs.com/blog/138012/201702/138012-20170228112237798-1507196643.png)
77+

part3-promise/02-promise-use.md

+195
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
# Promise 在 ES6 中的具体应用
2+
3+
上一节对 ES6 的 Promise 有了一个最简单的介绍,这一节详细说一下 Promise 那些最常见的功能
4+
5+
本节展示的代码参考[这里](./test.js)
6+
7+
## 本节课程概述
8+
9+
- 准备工作
10+
- 参数传递
11+
- 异常捕获
12+
- 串联多个异步操作
13+
- `Promise.all``Promise.race`的应用
14+
- `Promise.resolve`的应用
15+
- 其他
16+
17+
## 准备工作
18+
19+
因为以下所有的代码都会用到`Promise`,因此干脆在所有介绍之前,先封装一个`Promise`**封装一次,为下面多次应用**
20+
21+
```javascript
22+
const fs = require('fs')
23+
const path = require('path') // 后面获取文件路径时候会用到
24+
const readFilePromise = function (fileName) {
25+
return new Promise((resolve, reject) => {
26+
fs.readFile(fileName, (err, data) => {
27+
if (err) {
28+
reject(err) // 注意,这里执行 reject 是传递了参数,后面会有地方接收到这个参数
29+
} else {
30+
resolve(data.toString()) // 注意,这里执行 resolve 时传递了参数,后面会有地方接收到这个参数
31+
}
32+
})
33+
})
34+
}
35+
```
36+
37+
以上代码一个一段 nodejs 代码,将读取文件的函数`fs.readFile`封装为一个`Promise`。经过上一节的学习,我想大家肯定都能看明白代码的含义,要是看不明白,你就需要回炉重造了!
38+
39+
## 参数传递
40+
41+
我们要使用上面封装的`readFilePromise`读取一个 json 文件`../data/data2.json`,这个文件内容非常简单:`{"a":100, "b":200}`
42+
43+
先将文件内容打印出来,代码如下。大家需要注意,`readFilePromise`函数中,执行`resolve(data.toString())`传递的参数内容,会被下面代码中的`data`参数所接收到。
44+
45+
```javascript
46+
const fullFileName = path.resolve(__dirname, '../data/data2.json')
47+
const result = readFilePromise(fullFileName)
48+
result.then(data => {
49+
console.log(data)
50+
})
51+
```
52+
53+
再加一个需求,在打印出文件内容之后,我还想看看`a`属性的值,代码如下。之前我们已经知道`then`可以执行链式操作,如果`then`有多步骤的操作,那么前面步骤`return`的值会被当做参数传递给后面步骤的函数,如下面代码中的`a`就接收到了`return JSON.parse(data).a`的值
54+
55+
```javascript
56+
const fullFileName = path.resolve(__dirname, '../data/data2.json')
57+
const result = readFilePromise(fullFileName)
58+
result.then(data => {
59+
// 第一步操作
60+
console.log(data)
61+
return JSON.parse(data).a // 这里将 a 属性的值 return
62+
}).then(a => {
63+
// 第二步操作
64+
console.log(a) // 这里可以获取上一步 return 过来的值
65+
})
66+
```
67+
68+
总结一下,这一段内容提到的“参数传递”其实有两个方面:
69+
70+
- 执行`resolve`传递的值,会被第一个`then`处理时接收到
71+
- 如果`then`有链式操作,前面步骤返回的值,会被后面的步骤获取到
72+
73+
## 异常捕获
74+
75+
我们知道`then`会接收两个参数(函数),第一个参数会在执行`resolve`之后触发(还能传递参数),第二个参数会在执行`reject`之后触发(其实也可以传递参数,和`resolve`传递参数一样),但是上面的例子中,**我们没有用到`then`的第二个参数。这是为何呢 ———— 因为我没不建议这么用**
76+
77+
对于`Promise`中的异常处理,我们建议用`catch`方法,而不是`then`的第二个参数。请看下面的代码,以及注释。
78+
79+
```javascript
80+
const fullFileName = path.resolve(__dirname, '../data/data2.json')
81+
const result = readFilePromise(fullFileName)
82+
result.then(data => {
83+
console.log(data)
84+
return JSON.parse(data).a
85+
}).then(a => {
86+
console.log(a)
87+
}).catch(err => {
88+
console.log(err.stack) // 这里的 catch 就能捕获 readFilePromise 中触发的 reject ,而且能接收 reject 传递的参数
89+
})
90+
```
91+
92+
在若干个`then`串联之后,我们一般会在最后跟一个`.catch`来捕获异常,而且执行`reject`时传递的参数也会在`catch`中获取到。这样做的好处是:
93+
94+
- 让程序看起来更加简洁,是一个串联的关系,没有分支(如果用`then`的两个参数,就会出现分支,影响阅读)
95+
- 看起来更像是`try - catch`的样子,更易理解
96+
97+
## 串联多个异步操作
98+
99+
如果现在有一个需求:先读取`data2.json`的内容,当成功之后,再去读取`data1.json`。这样的需求,如果用传统的`callback`去实现,会变得很麻烦。而且,现在只是两个文件,如果是十几个文件这样做,写出来的代码就没法看了(臭名昭著的`callback-hell`)。但是用刚刚学到的`Promise`就可以轻松胜任这项工作
100+
101+
```javascript
102+
const fullFileName2 = path.resolve(__dirname, '../data/data2.json')
103+
const result2 = readFilePromise(fullFileName2)
104+
const fullFileName1 = path.resolve(__dirname, '../data/data1.json')
105+
const result1 = readFilePromise(fullFileName1)
106+
107+
result2.then(data => {
108+
console.log('data2.json', data)
109+
return result1 // 此处只需返回读取 data1.json 的 Promise 即可
110+
}).then(data => {
111+
console.log('data1.json', data) // data 即可接收到 data1.json 的内容
112+
})
113+
```
114+
115+
上文“参数传递”提到过,如果`then`有链式操作,前面步骤返回的值,会被后面的步骤获取到。**但是,如果前面步骤返回值是一个`Promise`的话,情况就不一样了 ———— 如果前面返回的是`Promise`对象,后面的`then`将会被当做这个返回的`Promise`的第一个`then`来对待** ———— 如果你这句话看不懂,你需要将“参数传递”的示例代码和这里的示例代码联合起来对比着看,然后体会这句话的意思。
116+
117+
## `Promise.all``Promise.race`的应用
118+
119+
我还得继续提出更加奇葩的需求,以演示`Promise`的各个常用功能。如下需求:
120+
121+
读取两个文件`data1.json``data2.json`,现在我需要一起读取这两个文件,等待它们全部都被读取完,再做下一步的操作。此时需要用到`Promise.all`
122+
123+
```javascript
124+
// Promise.all 接收一个包含多个 promise 对象的数组
125+
Promise.all([result1, result2]).then(datas => {
126+
// 接收到的 datas 是一个数组,依次包含了多个 promise 返回的内容
127+
console.log(datas[0])
128+
console.log(datas[1])
129+
})
130+
```
131+
132+
133+
读取两个文件`data1.json``data2.json`,现在我需要一起读取这两个文件,但是只要有一个已经读取了,就可以进行下一步的操作。此时需要用到`Promise.race`
134+
135+
```javascript
136+
// Promise.race 接收一个包含多个 promise 对象的数组
137+
Promise.race([result1, result2]).then(data => {
138+
// data 即最先执行完成的 promise 的返回值
139+
console.log(data)
140+
})
141+
```
142+
143+
## `Promise.resolve`的应用
144+
145+
从 jquery 引出,到此即将介绍完 ES6 的`Promise`,现在我们再回归到 jquery 。
146+
147+
大家都是到 jquery v1.5 之后`$.ajax()`返回的是一个`deferred`对象,而这个`deferred`对象和我们现在正在学习的`Promise`对象已经很接近了,但是还不一样。那么 ———— `deferred`对象能否转换成 ES6 的`Promise`对象来使用??
148+
149+
答案是能!需要使用`Promise.resolve`来实现这一功能,请看以下代码:
150+
151+
```javascript
152+
// 在浏览器环境下运行,而非 node 环境
153+
cosnt jsPromise = Promise.resolve($.ajax('/whatever.json'))
154+
jsPromise.then(data => {
155+
// ...
156+
})
157+
```
158+
159+
**注意:这里的`Promise.resolve`和文章最初`readFilePromise`函数内部的`resolve`函数可千万不要混了,完全是两码事儿**。JS 基础好的同学一看就明白,而这里看不明白的同学,要特别注意。
160+
161+
实际上,并不是`Promise.resolve`对 jquery 的`deferred`对象做了特殊处理,**而是`Promise.resolve`能够将`thenable`对象转换为`Promise`对象**。什么是`thenable`对象?———— 看个例子
162+
163+
```javascript
164+
// 定义一个 thenable 对象
165+
const thenable = {
166+
// 所谓 thenable 对象,就是具有 then 属性,而且属性值是如下格式函数的对象
167+
then: (resolve, reject) => {
168+
resolve(200)
169+
}
170+
}
171+
172+
// thenable 对象可以转换为 Promise 对象
173+
const promise = Promise.resolve(thenable)
174+
promise.then(data => {
175+
// ...
176+
})
177+
```
178+
179+
上面的代码就将一个`thenalbe`对象转换为一个`Promise`对象,只不过这里没有异步操作,所有的都会同步执行,但是不会报错的。
180+
181+
其实,在我们的日常开发中,这种将`thenable`转换为`Promise`的需求并不多。**真正需要的是,将一些异步操作函数(如`fs.readFile`)转换为`Promise`**(就像文章一开始`readFilePromise`做的那样)。这块,我们后面会在介绍`Q.js`库时,告诉大家一个简单的方法。
182+
183+
## 其他
184+
185+
以上都是一些日常开发中非常常用的功能,其他详细的介绍,请参考阮一峰老师的 [ES6 教程 Promise 篇](http://es6.ruanyifeng.com/#docs/promise)
186+
187+
最后,本节我们只是介绍了`Promise`的一些应用,通俗易懂拿来就用的东西,但是没有提升到理论和标准的高度。有人可能会不屑 ———— 我会用就行了,要那么空谈的理论干嘛?———— **你只会使用却上升不到理论高度,永远都是个搬砖的,搬一块砖挣一毛钱,不搬就不挣钱!** 在我看来,所有的知识应该都需要上升到理论高度,将实际应用和标准对接,知道真正的出处,才能走的长远。
188+
189+
下一节我们介绍 Promise/A+ 规范
190+
191+
## 求打赏
192+
193+
如果你看完了,感觉还不错,欢迎给我打赏 ———— 以激励我更多输出优质内容
194+
195+
![](http://images2015.cnblogs.com/blog/138012/201702/138012-20170228112237798-1507196643.png)

part3-promise/03-promise-standard.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# 对标一下 Promise/A+ 规范
2+
3+
4+
5+
## 求打赏
6+
7+
如果你看完了,感觉还不错,欢迎给我打赏 ———— 以激励我更多输出优质内容
8+
9+
![](http://images2015.cnblogs.com/blog/138012/201702/138012-20170228112237798-1507196643.png)

part3-promise/04-promise-callback.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Promise 真的取代 callback 了吗
2+
3+
4+
5+
## 求打赏
6+
7+
如果你看完了,感觉还不错,欢迎给我打赏 ———— 以激励我更多输出优质内容
8+
9+
![](http://images2015.cnblogs.com/blog/138012/201702/138012-20170228112237798-1507196643.png)

part3-promise/05-promise-q.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# 使用 Q.js 库
2+
3+
4+
5+
6+
## 求打赏
7+
8+
如果你看完了,感觉还不错,欢迎给我打赏 ———— 以激励我更多输出优质内容
9+
10+
![](http://images2015.cnblogs.com/blog/138012/201702/138012-20170228112237798-1507196643.png)

0 commit comments

Comments
 (0)