Node.js 的内置 util
包有一个 promisify()
函数 转换 回调 基于 Promise 函数的函数。 这使您可以将 Promise 链 和 async/await 与基于回调的 API 一起使用。
例如,Node.js fs
使用回调。 通常要读取文件,您需要使用回调:
const fs = require(fs);
fs.readFile(./package.json, function callback(err, buf) {
const obj = JSON.parse(buf.toString(utf8));
obj.name; // masteringjs.io
});
您可以使用 util.promisify()
转换 fs.readFile()
函数到返回回调的函数:
const fs = require(fs);
const util = require(util);
// Convert `fs.readFile()` into a function that takes the
// same parameters but returns a promise.
const readFile = util.promisify(fs.readFile);
// You can now use `readFile()` with `await`!
const buf = await readFile(./package.json);
const obj = JSON.parse(buf.toString(utf8));
obj.name; // masteringjs.io
假设
如何 util.promisify()
在引擎盖下工作? 有一个 polyfill ,你可以在 这里阅读完整的实现 。您也可以在 此处找到 Node.js 实现 ,尽管出于教育目的,polyfill 更易于阅读。
背后的关键思想 util.promisify()
就是它 给你传入的参数增加了一个回调函数 。该回调函数解析或拒绝承诺函数返回的承诺。这有点拗口,所以这是一个非常简化的自定义实现示例 util.promisify()
。
const fs = require(fs);
// A simplified implementation of `util.promisify()`. Doesnt
// cover all cases, dont use this in prod!
function promisify(fn) {
return function() {
const args = Array.prototype.slice.call(arguments);
return new Promise((resolve, reject) => {
fn.apply(this, [].concat(args).concat([(err, res) => {
if (err != null) {
return reject(err);
}
resolve(res);
}]));
});
};
}
// Convert `fs.readFile()` into a function that takes the
// same parameters but returns a promise.
const readFile = promisify(fs.readFile);
// You can now use `readFile()` with `await`!
const buf = await readFile(./package.json);
const obj = JSON.parse(buf.toString(utf8));
obj.name; // masteringjs.io
那么这是什么意思?
第一 util.promisify()
向您传入的参数添加 1 个额外参数,然后使用这些新参数调用原始函数。 这意味着底层函数需要支持该数量的参数。 所以如果你正在调用一个 Promise 函数 myFn()
有 2 个类型的参数 [String, Object]
,确保原始函数支持调用签名 [String, Object, Function]
。
第二 util.promisify()
对 函数上下文 。
失去上下文
丢失上下文 意味着函数调用以错误的值结束 this
, 丢失上下文是转换函数的常见问题:
class MyClass {
myCallbackFn(cb) {
cb(null, this);
}
}
const obj = new MyClass();
const promisified = require(util).promisify(obj.myCallbackFn);
const context = await promisified();
context; // `undefined` instead of a `MyClass` instance!
请记住 this
包含该函数在调用时的属性的任何对象。 因此您可以通过将 promisified 函数设置为同一对象的属性来保留上下文:
class MyClass {
myCallbackFn(cb) {
cb(null, this);
}
}
const obj = new MyClass();
// Retain context because `promisified` is a property of `obj`
obj.promisified = require(util).promisify(obj.myCallbackFn);
const context = await obj.promisified();
context === obj; // true
请登录后查看评论内容