Node.js 8 于上个月月底正式发布,带来了很多新特性。其中比较值得注意的,便有 util.promisify()
这个方法。
如果你已经很熟悉 Promise,请继续往下看。如果你还不熟悉 Promise,可以先跳过去看下下章:Promise 介绍。
util.promisify()
虽然 Promise 已经普及,但是 Node.js 里仍然有大量依赖回调的异步函数,如果我们把每个函数都封装一遍,那真是齁麻烦齁麻烦的,比齁还麻烦。
所以 Node.js 8 就提供了 util.promisify()
这个方法,方便我们把原来的异步回调方法改成支持 Promise 的方法,接下来,想继续 .then().then().then()
搞队列,还是 await 就看实际需要了。
我们看下范例,让读取目录文件状态的 fs.stat
支持 Promise:
const util = require('util');
const fs = require('fs');
const stat = util.promisify(fs.stat);
stat('.')
.then((stats) => {
// Do something with `stats`
})
.catch((error) => {
// Handle the error.
});
怎么样,很简单吧?按照文档的说法,只要符合 Node.js 的回调风格,所有函数都可以这样转换。也就是说,只要满足下面两个条件,无论是不是原生方法,都可以:
- 最后一个参数是回调函数
- 回调函数的参数为
(err, result)
,前面是可能的错误,后面是正常的结果
结合 Await/Async 使用
同样是上面的例子,如果想要结合 Await/Async,可以这样使用:
const util = require('util');
const fs = require('fs');
const stat = util.promisify(fs.stat);
async function readStats(dir) {
try {
let stats = await stat(dir);
// Do something with `stats`
} catch (err) { // Handle the error.
console.log(err);
}
}
readStats('.');
自定义 Promise 化处理函数
那如果现有的使用回调的函数不符合这个风格,还能用 util.promisify()
么?答案也是肯定的。我们只要给函数增加一个属性 util.promisify.custom
,指定一个函数作为 Promise 化处理函数,即可。请看下面的代码:
const util = require('util');
// 这就是要处理的使用回调的函数
function doSomething(foo, callback) {
// ...
}
// 给它增加一个方法,用来在 Promise 化时调用
doSomething[util.promisify.custom] = function(foo) {
// 自定义生成 Promise 的逻辑
return getPromiseSomehow();
};
const promisified = util.promisify(doSomething);
console.log(promisified === doSomething[util.promisify.custom]);
// prints 'true'
如此一来,任何时候我们对目标函数 doSomething 进行 Promise 化处理,都会得到之前定义的函数。运行它,就会按照我们设计的特定逻辑返回 Promise 实例。
我们就可以升级以前所有的异步回调函数了。
Promise 介绍
因为种种历史原因,JS 当中有大量异步函数。这些异步函数,大多要依赖回调进行处理(这里我觉得把事件侦听算作回调也是合理的),但是回调嵌套层次一多,就会形成所谓的“回调陷阱”,让开发者苦不堪言。
为了解决这个问题,开发社区经过摸索,总结出来一套名为 Promise/A+ 的解决方案。大体上来说,这套方案通过使用 “Promise 回调实例”包裹原先的回调函数,可以将原先复杂的嵌套展开、铺平,从而降低开发和维护的难度和成本。
new Promise( (resolve, reject) => { // 构建一个 Promise 实例
someAsyncFunction( (err, result) => { // 调用原来的异步函数
if (err) { // 发生错误,进入错误处理模式
return reject(err);
}
resolve(result); // 一切正常,进入队列的下一环节
});
})
.then( result => { // 下一环节
return doSomething(result);
})
.then( result2 => { // 又下一环节
return doSomething2(result2);
})
... // 各种中间环节
.catch( err => { // 错误处理
console.log(err);
});
ES2015(ES6)里包含了 Promise 标准,如今已经在大部分运行时里实装,我们可以放心大胆的使用它。而且,由于 Promise 不需要新的语法元素,所以即使在不支持原生 Promise 的环境里也可以使用类库,比如 Q 或者 Bluebird,甚至 jQuery。
在小程序里也有效哟!
ES2017 增加了 Await/Async 语法,但请注意,Await
后面必须跟 Promise 实例才能实现异步。所以,大家还是把 Promise 的概念学好吧!
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function f1() {
var x = await resolveAfter2Seconds(10);
console.log(x); // 10
}
f1();
例子来源于 MDN。
如果你想进一步学习使用 Promise,强烈推荐我的这次分享:Promise 的 N 种用法。可以帮助你一站式的学会使用 Promise。
PS2:刚才看到 Node.js 已经发布 8.1 了,真快呀……新版本的 Changelog 在这里,已修复为主。
欢迎吐槽,共同进步