利用 Node.js 的 promisify 将回调转 Promise

Node.js 中存在大量回调形式的 API,虽然后续的版本中大部分已经对应提供了 Promise 版本。

通过 util.promisify 可将回调形式的 API 转 promise。

fs.stat 为例,下面是来自官方文档中的演示:

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.
  });

转 promise 的条件

util.promisify 假定被转函数最后一个参数为 Node.js 中错误优先的回调形式,即,最后一个入参长这样 (err, value) => ...

Node.js 的 API 大多遵循这种风格,所以这样的假设能满足大部分场景,但不是全部。

自定义 Promise 化的函数

不满足的情况下可通过自定义的试来操作。

具体来说,假如被转函数身上存在一个名叫 util.promisify.custom 的属性,调用 promisify 后会返回该属性的值。

例如,被转函数为

const original = (arg1, arg2,...)=> ...

同时 original[util.promisify.custom] 存在,那么:

original[util.promisify.custom] === util.promisify(original);

上面的操作有什么用?

这个 util.promisify.custom 是 Node.js 提供的符号,作为名称在这里使用而以,没什么特别。

假设有如下回调形式的函数:

function doSomething(data, onSuccessCallback, onErrorCallback)

通过自定义 promisify 的返回,将其转成 Promise 调用。

doSomething[util.promisify.custom] = (foo) => {
  return new Promise((resolve, reject) => {
    doSomething(foo, resolve, reject);
  });
};

const promisified = util.promisify(doSomething);

promisified(foo)
  .then(()=>console.log('success))
  .catch((err) => console.log(err));

更加 General 的情况

推而广之,如果不考虑错误处理,函数第一个参数是不是错误就无所谓了,任何回调形式都可以改成 Promsie,比如 setTimeout。

const { promisify } = require("util");

const sleep = promisify(setTimeout);

const main = async () => {
  console.time("sleep");
  await sleep(1000);
  console.timeEnd("sleep");
};

main();

像上面,我们压根就不关心调用 setTimeout 之后的返回,因此就不涉及它内部的流程处理,直接利用了其 Promise 特性而以。

相关资源