Promisification 是指将基于回调的函数转换为返回 Promise 的函数的过程。在 JavaScript 中,这是一种常见的模式,特别是在现代异步编程中,Promise 和 async/await 已经成为处理异步操作的首选方式。
Promisification 的核心思想是:
回调风格:
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(null, script);
script.onerror = () => callback(new Error(`Script load error for ${src}`));
document.head.append(script);
}
// 使用
loadScript('path/script.js', (err, script) => {
if (err) {
// 处理错误
} else {
// 脚本加载成功
}
});
Promise 风格(Promisified):
function loadScriptPromise(src) {
return new Promise((resolve, reject) => {
loadScript(src, (err, script) => {
if (err) reject(err);
else resolve(script);
});
});
}
// 使用
loadScriptPromise('path/script.js')
.then(script => {
// 脚本加载成功
})
.catch(err => {
// 处理错误
});
对于单个函数,可以手动包装:
function promisify(f) {
return function(...args) { // 返回一个包装函数
return new Promise((resolve, reject) => {
function callback(err, result) { // 自定义回调
if (err) {
reject(err);
} else {
resolve(result);
}
}
args.push(callback); // 将回调函数附加到参数末尾
f.call(this, ...args); // 调用原始函数
});
};
}
// 使用
let loadScriptPromise = promisify(loadScript);
loadScriptPromise('path/script.js').then(...);
Node.js 提供了内置的 util.promisify
方法:
const util = require('util');
const fs = require('fs');
const readFile = util.promisify(fs.readFile);
readFile('file.txt', 'utf8')
.then(text => console.log(text))
.catch(err => console.error('Error:', err));
有些回调会返回多个值,这时可以 resolve 一个数组或对象:
function promisifyMulti(f) {
return function(...args) {
return new Promise((resolve, reject) => {
function callback(err, ...results) {
if (err) {
reject(err);
} else {
resolve(results.length === 1 ? results[0] : results);
}
}
args.push(callback);
f.call(this, ...args);
});
};
}
当需要 promisify 对象的方法时,需要保留 this
上下文:
const promisifiedMethod = promisify(someObject.method.bind(someObject));
有时也需要将 Promise 转换为回调风格:
function callbackify(f) {
return function(...args) {
const callback = args.pop();
f(...args)
.then(result => callback(null, result))
.catch(err => callback(err));
};
}
(err, result)
在 ES6+ 环境中,许多 API 已经提供了 Promise 版本(如 fs.promises
),优先使用这些原生 Promise API 而不是手动 promisify。
Promisification 是 JavaScript 异步编程演进中的重要模式,它帮助开发者更优雅地处理异步操作,特别是在从回调模式向 Promise/async-await 迁移的过程中。