前言
本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。 这是源码共读的第14期,链接:juejin.cn/post/708315…
简介
将回调函数转换为Promises函数
使用
让我们用官网案例简单了解一下
const {promisify} = require("es6-promisify");
const fs = require("fs");
// 调用promisify返回一个新函数
const stat = promisify(fs.stat);
// Now usable as a promise!
try {
// 可以直接用await 接收返回值
const stats = await stat("example.txt");
console.log("Got stats", stats);
} catch (err) {
console.error("Yikes!", err);
}
源码
- promisify接收一个original函数传参,返回一个函数function, 在返回的这个function里面包裹一个Promise对象,并将Promise的resove函数和reject函数裹在callback函数中,再把callback函数传参给original函数内部调用, 拿到值后resove结果
- original缓存到promisify函数作用域中,promisify方法再返回一个函数,函数内部又调用original,形成了一个闭包
const customArgumentsToken = "__ES6-PROMISIFY--CUSTOM-ARGUMENTS__";
/**
* promisify()
* 转换基于回调的函数 -- func(arg1, arg2 .. argN, callback) --
* 为ES6-compatible Promise. Promisify 提供了一个默认回调函数形式 (error, result)
* @param {function} original - 将要转化为promisify函数的function
* @return {function} A promisified version of `original`
*/
export function promisify(original) {
// original判断,
if (typeof original !== "function") {
throw new TypeError("Argument to promisify must be a function");
}
const argumentNames = original[customArgumentsToken];
// 存在用户自定义Promise,则优先获取
const ES6Promise = promisify.Promise || Promise;
// If we can find no Promise implemention, then fail now.
if (typeof ES6Promise !== "function") {
throw new Error("No Promise implementation found; do you need a polyfill?");
}
// 返回一个函数
return function (...args) {
return new ES6Promise((resolve, reject) => {
// args数组里增加一个callback回调函数
args.push(function callback(err, ...values) {
if (err) {
return reject(err);
}
// values.length为1 或者没有argumentNames,直接resove
if (values.length === 1 || !argumentNames) {
return resolve(values[0]);
}
// 存在自定义属性,则赋值在一个对象o中
const o = {};
values.forEach((value, index) => {
const name = argumentNames[index];
if (name) {
o[name] = value;
}
});
// resolve这个o对象
resolve(o);
});
// 执行original函数, 将args传参传入,此时的args存在callback函数
original.apply(this, args);
});
};
}
// 将argumentNames和Promise属性挂载到promisify函数上,这样用户可以直接使用
promisify.argumentNames = customArgumentsToken;
promisify.Promise = undefined;
promisify简单实现
const loadScript = (url, callback) => {
const script = document.createElement('script')
script.src = url
script.onload = () => callback()
script.onerror = () => callback(new Error(`Script load error for ${url}`))
}
const promisefy = (fn) => {
return (...args) => {
return new Promise((resolve, reject) => {
args.push((error) => {
if(error instanceof Error) return reject(error)
resolve()
})
// 执行fn函数
fn.apply(this, args)
})
}
}
const loads = promisefy(loadScript)
const res = await loads('https://example.com/script.js').catch(err => {
console.log(err)
})
总结
对promise使用又熟悉了,我们可以把resove或者reject函数传参到自定义函数中实现各种逻辑,欢迎大家指出问题~完结撒花