如何把 Node.js 的回调函数 API 转换为 Promise?

把回调转 Promise:手动用 new Promise 包装(error-first 回调调 reject/resolve),或用 Node 内置 util.promisify 一行转换。本文给出两种方法、参数处理和常见坑。

最佳实践
Node.js 事件循环与异步任务示意

两种方法:手动 new Promise 包装,或直接用内置 util.promisify() 一键转换。

方法一:util.promisify(推荐)

Node 回调遵循 error-first 约定 callback(err, result) 的函数,一行搞定:

const util = require('util');
const fs = require('fs');

const readFile = util.promisify(fs.readFile);

readFile('a.txt', 'utf8').then(console.log).catch(console.error);

方法二:手动包装(理解原理)

// 原始回调式函数
function fetchData(callback) {
  setTimeout(() => {
    callback(null, { result: '一些数据' });
  }, 1000);
}

// 包装成 Promise
function fetchDataPromise() {
  return new Promise((resolve, reject) => {
    fetchData((error, data) => {
      if (error) reject(error);   // error-first:有错 reject
      else resolve(data);          // 成功 resolve
    });
  });
}

// 现在可以 await 了
fetchDataPromise().then(console.log).catch(console.error);

要点:回调的第一个参数是错误——有 err 走 reject,没 err 走 resolve,顺序别反。

特殊情况处理

回调有多个成功值(如 callback(null, user, meta)):

// promisify 默认只取第一个成功值;自定义取全部
fn[util.promisify.custom] = (a) => new Promise((res, rej) => {
  fn(a, (err, user, meta) => err ? rej(err) : res({ user, meta }));
});
const custom = util.promisify(fn); // 先定义自定义实现,再转换

手动包装则直接在 resolve 里组装:resolve({ user, meta })。

this 依赖:有些函数依赖调用时的 this(如对象方法),用 promisify 时 .call(obj, ...) 或先 bind:util.promisify(obj.method).bind(obj)。

常见问题(FAQ)

Q:Promise 化之后还能用回调调用吗?
Promise 版函数不再接受回调参数。要同时支持两种风格,得在函数里检测最后一个参数是否为函数,不建议在新代码这么做。

Q:fs 模块还要手动 promisify 吗?
不用。require('fs').promises 提供全套 Promise 版本,直接用。

Q:转成 Promise 后错误处理怎么写?
用 async/await 就配 try/catch;用链式就 .catch()。注意别混用回调和 Promise 两种错误处理,容易漏捕获。

参考资料

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台