Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 8096cec

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

File tree

8 files changed

+200
-187
lines changed

8 files changed

+200
-187
lines changed

‎02-3-node-js.md‎

Lines changed: 0 additions & 78 deletions
This file was deleted.

‎04-01-downgrade.md‎

Lines changed: 2 additions & 2 deletions
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, {
Lines changed: 8 additions & 6 deletions
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

‎04-3-other.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
其它场景
2+
========

‎04-lets-do-it.md‎

Lines changed: 172 additions & 0 deletions
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)

AltStyle によって変換されたページ (->オリジナル) /