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
© 版权声明
评论 抢沙发

请登录后发表评论