一、前言
我最近参加了源码共读活动,感兴趣的和我一起学习源码吧.
我们将学到
1. promisify的作用
2. Node源码中 promisify的使用及实现
3. 自己简单实现 promisify 函数
4. 通用型 promisify 函数
二、正文
1. promisify的作用
在实际的项目需求中,经常会遇到函数需要链式调用的情况
而在ES6之前,基本采用的是callback回调的形式,
在ES6提供了原生Promise 对象后,更多的前端同学喜欢构造一个Promise对象来实现需求
例如
function ajax(URL) {
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
resolve(req.responseText);
} else {
reject(new Error(req.statusText));
}
};
req.onerror = function () {
reject(new Error(req.statusText));
};
req.send();
});
}
var URL = "/try/ajax/testpromise";
ajax(URL).then(function onFulfilled(value){
document.write('内容是:' + value);
}).catch(function onRejected(error){
document.write('错误:' + error);
});
promisify函数作用就是把 callback 形式转成 promise 形式。
2. promisify的使用
const util = require('util');
class Foo {
constructor() {
this.a = 42;
}
bar(callback) {
// 在Node中,很多地方参数处理首个参数是错误信息回调
callback(null, this.a);
}
}
const foo = new Foo();
const naiveBar = util.promisify(foo.bar);
naiveBar.call(foo).then((a) => console.log(a)); // '42'
3. promisify Node源码
Node utils promisify 源码
const kCustomPromisifiedSymbol = SymbolFor('nodejs.util.promisify.custom');
const kCustomPromisifyArgsSymbol = Symbol('customPromisifyArgs');
let validateFunction;
function promisify(original) {
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
});
}
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;
4. 简单实现 promisify
function promisify(fn) {
console.log(fn,"fn");
return function (...args) {
console.log(...args,"...args");
返回promise对象
return new Promise(function (resolve, reject) {
args.push(function (err, ...arg) {
console.log(...args,"12");
if (err) {
reject(err);
return;
}
resolve(...arg);
});
fn.apply(null, args);
});
}
}
let add = (a,b, callback) => {
let result = a+b;
if(typeof result === 'number') {
callback(null,result)
}else {
callback("请输入正确数字")
}
}
const addCall = promisify(add);
addCall(2,6).then((res) => {
console.log(res);
})
通过打印信息可以看出promisify内部原理: 保存原始函数,将callback函数添加到参数末尾进行执行,将结果通过promise对象返回
5. 通用型 promisify
function promisify(original) {
function fn(...args) {
return new Promise((resolve, reject) => {
args.push((err, ...values) => {
if (err) {
return reject(err);
}
resolve(values);
});
Reflect.apply(original, this, args);
});
}
return fn;
}
示例
let add = (a,b, callback) => {
let result = a+b;
if(typeof result === 'number') {
callback(null,result)
}else {
callback("请输入正确数字")
}
}
const addCall = promisify(add);
async function load() {
try{
const res = await addCall(2,6)
console.log(res);
}catch(err){
console.log(err);
}
}
load()
6. Reflect.apply 与 Function.apply 的区别
- Reflect.apply(target, thisArgument, argumentsList)
- target 目标函数
- thisArgument 执行上下文
- argumentsList 参数
- Reflect.apply 实现与 Function.prototype.apply()方法类似
- Function.prototype.apply.call(Math.floor, undefined, [1.75]);
- Function .apply(thisArgument, [argsArray])
- Function 目标函数
- thisArgument 执行上下文
- argsArray 参数,可选
三、总结
- 通过学习Node中对异步函数处理,将callback形式的函数转换成promise形式的函数,使得开发代码更易读和易维护。
- 可以放在在日常开发函数库中,在遇到需要链式调用的需求中直接使用。
- 感兴趣的同学可以关注下前端小溪公众号
|