Skip to content

Commit 8096cec

Browse files
committed
add some content
1 parent 2ea4295 commit 8096cec

8 files changed

+200
-187
lines changed

Diff for: 02-3-node-js.md

-78
This file was deleted.

Diff for: 04-01-downgrade.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
## jQuery
1111

12-
jQuery 早在 1.5 版本就开始这方面的探索,已经实现了 Promise,不过名字不太一样,[Deferred 对象](http://api.jquery.com/category/deferred-object/)。实现也不太一样,因为 jQuery 1.5 之后就开始这方面的尝试,所以和最终规范肯定有出入。不过升级到 [3.0](http://blog.meathill.com/tech/js/jquery/jquery-3-0-beta-released.html) 之后,它就完全遵守规范,并且也通过了测试。所以如果使用新版本,我们大可以按照之前的教程来操作,只是 jQuery 需要使用工厂方法来创建 Promise 实例,与规范略有区别
12+
jQuery 早在 1.5 版本就开始这方面的探索,不过它起的名字不太一样,[Deferred 对象](http://api.jquery.com/category/deferred-object/)。实现也不太一样,有一些特殊的 API,比如 [.done()](http://api.jquery.com/deferred.done/)。不过升级到 [3.0](http://blog.meathill.com/tech/js/jquery/jquery-3-0-beta-released.html) 之后,jQuery 就完全兼容 ES2015 的 Promise 规范,并且通过了相关测试。所以如果使用新版本,我们大可以按照前面的教程来操作,只是 jQuery 需要使用工厂方法来创建 Promise 实例,与标准做法略有区别
1313

1414
```javascript
1515
$.deferred(function (resolve) {
@@ -20,7 +20,7 @@ $.deferred(function (resolve) {
2020
});
2121
```
2222

23-
另外,jQuery 的 [jqXHR](http://api.jquery.com/jQuery.ajax/#jqXHR) 对象也是 Promise 对象,所以完全可以用 `.then()` 方法操作:
23+
至于那些独有 API,我们当它们不存在就好了。另外,jQuery 的 [jqXHR](http://api.jquery.com/jQuery.ajax/#jqXHR) 也是 Promise 对象,所以完全可以用 `.then()` 方法操作:
2424

2525
```javascript
2626
$.ajax(url, {

Diff for: 20-1-xiaochengxu.md renamed to 04-2-xiaochengxu.md

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
在小程序中使用 Promise
22
========
33

4-
小程序里由于要跟原生应用做交互,大部分 API 都是用异步回调实现的,所以自然而然的,我就想用更好的方式去操作
4+
国内开发者尤其是前端,肯定不能避开“小程序”这个话题
55

6-
因为客户端的 WebView 中不包含原生 Promise,所以“微信 Web 开发工具”中也移除了对 Promise 的支持,需要我们自己处理
6+
[小程序 API 文档](https://mp.weixin.qq.com/debug/wxadoc/dev/api/)可以看出,大部分交互都要藉由异步回调来完成(我猜测这多半是跟原生应用交互导致的)。所以自然而然的,我也想用更好的方式去操作
77

8-
如同之前所说,Promise 不需要引入新的语言元素,自然兼容性上佳,所以我们只要引用成熟的 Promise 类库就好。这里我选择的是 [Bluebird](http://bluebirdjs.com/)
8+
因为客户端的 WebView 中不支持原生 Promise,所以“微信 Web 开发工具”中也移除了对 Promise 的支持,需要我们自己处理。
9+
10+
好在正如之前所说,Promise 不需要引入新的语言元素,兼容性上佳,所以我们只要引用成熟的 Promise 类库就好。这里我选择 [Bluebird](http://bluebirdjs.com/)
911

1012
## 安装
1113

@@ -52,7 +54,7 @@ new Promise( resolve => {
5254
});
5355
});
5456

55-
// 这样做是可以的
57+
// 推荐这样做
5658
new Promise( (resolve, reject) => {
5759
wx.checkSession({
5860
success() {
@@ -63,13 +65,13 @@ new Promise( (resolve, reject) => {
6365
}
6466
});
6567
});
66-
```
68+
```
6769

6870
## Await/Async
6971

7072
“微信 Web 开发者工具”里面集成了 Babel 转译工具,可以将 ES6 编译成 ES5,不过 Await/Async 就不支持了。此时我们可以选择自行编译,或者只使用 Promise。
7173

72-
自行编译时,请注意,小程序页面没有 `<script>`,只能引用同名 `.js`所以要留神输出的文件名哟。这里建议把 JS 写在另一个文件夹,然后用 Babel 转译的时候再写过来
74+
自行编译时,请注意,小程序页面没有 `<script>`,只能引用同名 `.js`所以要留神输出的文件名。这里建议把 JS 写在另一个文件夹,然后用 Babel 转译,把最终文件写过来
7375

7476
```bash
7577
babel /path/to/my-xcx-src -d /path/to/my-xcx --source-map --watch

Diff for: 04-3-other.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
其它场景
2+
========

Diff for: 04-lets-do-it.md

+172
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
一起实战吧
2+
========
3+
4+
学会新的异步操作之后,我们自然希望改造之前的异步回调代码。下面我就带领大家来试一试。
5+
6+
## 将回调包装成 Promise
7+
8+
这是最常见的应用。它有三个显而易见的好处:
9+
10+
1. 可读性更好
11+
2. 返回的结果可以加入任何 Promise 队列
12+
3. 可以使用 Await/Async
13+
14+
我们就拿读取文件来举个例子。
15+
16+
```javascript
17+
// FileSystem.js
18+
const fs = require('fs');
19+
20+
module.exports = {
21+
readFile: function (path, options) {
22+
return new Promise( resolve => {
23+
fs.readFile(path, options, (err, content) => {
24+
if (err) {
25+
throw err;
26+
}
27+
resolve(content);
28+
});
29+
});
30+
}
31+
};
32+
33+
// promise.js
34+
const fs = require('./FileSystem');
35+
36+
fs.readFile('../README.md', 'utf-8')
37+
.then(content => {
38+
console.log(content);
39+
});
40+
41+
// async.js
42+
const fs = require('.FileSystem');
43+
44+
async function read(path) {
45+
let content = await fs.readFile(path);
46+
console.log(content);
47+
}
48+
read();
49+
```
50+
51+
## 将任何异步操作包装成 Promise
52+
53+
不止回调,其实我们可以把任意异步操作都包装城 Promise。我们假设需求:
54+
55+
1. 弹出确认窗口,用户点击确认再继续,点击取消就中断
56+
2. 由于样式的关系,不能使用 `window.confirm()`
57+
58+
之前我们的处理方式通常是:
59+
60+
```javascript
61+
something.on('done', function () { // 先做一些处理
62+
popup = openConfirmPopup('确定么'); // 弹出确认窗口
63+
popup.on('confirm', function goOn () { // 用户确认后继续处理
64+
// 继续处理
65+
});
66+
});
67+
```
68+
69+
如今,借助 Promise 的力量,我们可以把弹窗封装成 Promise,然后就可以将其融入队列,或者简单的使用 Async 等待操作完成。
70+
71+
```javascript
72+
function openConfirmPopup(msg) {
73+
let popup = createPopup(msg);
74+
return new Promise( (resolve, reject) => {
75+
popup.confirmButton.onclick = resolve;
76+
popup.cancelButton.onclick = reject;
77+
});
78+
}
79+
80+
// pure promise
81+
doSomething()
82+
.then(() => {
83+
return openConfirmPopup('确定么')
84+
})
85+
.then( () => {
86+
// 继续处理
87+
});
88+
89+
// async/await
90+
await doSomething();
91+
if (await openConfirmPopup('确定么')) {
92+
// 继续处理
93+
}
94+
```
95+
96+
## Node.js 8 的新方法
97+
98+
Node.js 8 于今年5月底正式发布,带来了[很多新特性](https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V8.md#8.0.0)。其中,`util.promisify()`,尤其值得我们注意。
99+
100+
为保证向下兼容,Node.js 里海量的异步回调函数自然都会保留,如果我们把每个函数都封装一遍,那真是齁麻烦齁麻烦,比齁还麻烦。
101+
102+
所以 Node.js 8 就提供了 `util.promisify()` ——“Promise 化”方法,方便我们把原来的异步回调方法瞬间改造成支持 Promise 的方法。接下来,想继续 `.then().then().then()` 搞队列,还是 Await 就看实际需要了。
103+
104+
> [Bluebird](http://bluebirdjs.com/) 也提供了类似的方法,不妨看下它的[文档](http://bluebirdjs.com/docs/features.html#promisification-on-steroids)
105+
106+
我们看下官方范例。已知读取目录文件状态的 `fs.stat`,想得到支持 Promise 的版本,只需要这样做:
107+
108+
```javascript
109+
const util = require('util');
110+
const fs = require('fs');
111+
112+
const stat = util.promisify(fs.stat);
113+
stat('.')
114+
.then((stats) => {
115+
// Do something with `stats`
116+
})
117+
.catch((error) => {
118+
// Handle the error.
119+
});
120+
```
121+
122+
怎么样,很简单吧?按照文档的说法,只要符合 Node.js 的回调风格,所有函数都可以这样转换。也就是说,只要满足下面两个条件,无论是不是原生方法,都可以:
123+
124+
1. 最后一个参数是回调函数
125+
2. 回调函数的参数为 `(err, result)`,前面是可能的错误,后面是正常的结果
126+
127+
### 结合 Await/Async 使用
128+
129+
同样是上面的例子,如果想要结合 Await/Async,可以这样使用:
130+
131+
```javascript
132+
const util = require('util');
133+
const fs = require('fs');
134+
135+
const stat = util.promisify(fs.stat);
136+
async function readStats(dir) {
137+
try {
138+
let stats = await stat(dir);
139+
// Do something with `stats`
140+
} catch (err) { // Handle the error.
141+
console.log(err);
142+
}
143+
}
144+
readStats('.');
145+
```
146+
147+
### 自定义 Promise 化处理函数
148+
149+
那如果现有的使用回调的函数不符合这个风格,还能用 `util.promisify()` 么?答案也是肯定的。我们只要给函数增加一个属性 `util.promisify.custom`,指定一个函数作为 Promise 化处理函数,即可。请看下面的代码:
150+
151+
```javascript
152+
const util = require('util');
153+
154+
// 这就是要处理的使用回调的函数
155+
function doSomething(foo, callback) {
156+
// ...
157+
}
158+
159+
// 给它增加一个方法,用来在 Promise 化时调用
160+
doSomething[util.promisify.custom] = function(foo) {
161+
// 自定义生成 Promise 的逻辑
162+
return getPromiseSomehow();
163+
};
164+
165+
const promisified = util.promisify(doSomething);
166+
console.log(promisified === doSomething[util.promisify.custom]);
167+
// prints 'true'
168+
```
169+
170+
如此一来,任何时候我们对目标函数 doSomething 进行 Promise 化处理,都会得到之前定义的函数。运行它,就会按照我们设计的特定逻辑返回 Promise 对象。
171+
172+
有了 `util.promisify`,升级异步回到函数,使用 Promise 或者 Async 真的方便了很多。

0 commit comments

Comments
 (0)