1 本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
【若川视野 x 源码共读】第14期 | promisify 【若川视野 x 源码共读】第14期 | promisify - 掘金 (juejin.cn)
2.callback callback callback 一定要记得是callback
2.1简化版
const imageSrc = 'https://www.themealdb.com/images/ingredients/Lime.png';
function loadImage(src, callback) {
const image = document.createElement('img');
image.src = src;
image.alt = '公众号若川视野专用图?';
image.style = 'width: 200px;height: 200px';
image.onload = () => callback(null, image);
image.onerror = () => callback(new Error('加载失败'));
document.body.append(image);
}
function promisify(original){
function fn(...args){
return new Promise((resolve, reject) => {
args.push((err, ...values) => {
if(err){
return reject(err);
}
resolve(values);
});
// original.apply(this, args);
Reflect.apply(original, this, args);
});
}
return fn;
}
const loadImagePromise = promisify(loadImage);
async function load(){
try{
const res = await loadImagePromise(imageSrc);
console.log(res);
}
catch(err){
console.log(err);
}
}
load();
loadImage.js文件创建一张图片并进行挂载到页面上,需要注意的是onload和onerror其箭头函数返回的是cb回调i函数;
promisify.js文件中Reflect.apply(original, this, args);Reflect其实和Proxy可以对对象的属性进行拦截,注意的是第三个参数传入一个箭头函数,该箭头函数会在图片请求成功进行执行onload的内callback,而这个时候就会再次执行传入前的箭头函数,如果错误就进行调用err. (callback 在开发中也经常碰到类似场景,比如在一个特殊弹窗组件A封装到一个B组件中,正常情况下是在组件A进行打开弹窗,但是现在突然也需要这样一个弹窗组件A,通过是否确定执行下一步,而我正常操作是在打开组件A传入箭头函数并保存,在弹窗A点击确定的时候通过判断是否有cb)
整体代码
const kCustomPromisifyArgsSymbol = Symbol('customPromisifyArgs');
let validateFunction;
function promisify(original) {
// Lazy-load to avoid a circular dependency.
if (validateFunction === undefined)
({ validateFunction } = require('internal/validators'));
validateFunction(original, 'original');
if (original[kCustomPromisifiedSymbol]) {
const fn = original[kCustomPromisifiedSymbol];
validateFunction(fn, 'util.promisify.custom');
return ObjectDefineProperty(fn, kCustomPromisifiedSymbol, {
value: fn, enumerable: false, writable: false, configurable: true
});
}
// Names to create an object from in case the callback receives multiple
// arguments, e.g. ['bytesRead', 'buffer'] for fs.read.
const argumentNames = original[kCustomPromisifyArgsSymbol];
function fn(...args) {
return new Promise((resolve, reject) => {
ArrayPrototypePush(args, (err, ...values) => {
if (err) {
return reject(err);
}
if (argumentNames !== undefined && values.length > 1) {
const obj = {};
for (let i = 0; i < argumentNames.length; i++)
obj[argumentNames[i]] = values[i];
resolve(obj);
} else {
resolve(values[0]);
}
});
ReflectApply(original, this, args);
});
}
ObjectSetPrototypeOf(fn, ObjectGetPrototypeOf(original));
ObjectDefineProperty(fn, kCustomPromisifiedSymbol, {
value: fn, enumerable: false, writable: false, configurable: true
});
return ObjectDefineProperties(
fn,
ObjectGetOwnPropertyDescriptors(original)
);
}
promisify.custom = kCustomPromisifiedSymbol;