通用 promisify 函数
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;
}
promisify函数是把 callback 形式转成 promise 形式。
为什么要这么写呢?
因为Node.js 天生异步,错误回调的形式书写代码。回调函数的第一个参数是错误信息。也就是错误优先。
一般的node异步方法是这样的:
fs.readFile('./data.json', 'utf-8', function(error, data) {
if (error) {console.log(error)}
console.log(data)
})
一般是最后一个参数作为回调函数,而回调函数有两个参数,第一个是error,第二个是data,错误优先。
而上面的promisify方法则是将类似readFile这种类型的方法转为promise,使其能进行链式处理回调。
示例:
如未使用promisify,则是:
const fs=require('fs');
const {resolve} = require("./webpack/webpack.config");
fs.readFile('./data.json', 'utf-8', function(error, data) {
if (error) {console.log(error)}
//[ '{\r\n "name": "tom"\r\n}\r\n' ]
console.log(data)
})
如果使用了prmisify,则是:
const fs=require('fs');
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 readFileAsync=promisify(fs.readFile)
readFileAsync('./data.json','utf-8').then(res=>{
//{"name": "tom"}
console.log(res)
}).catch(err=>{
console.log(err)
})
promisify 方法传入一个需要promise化的函数function,而这个函数的最后一个参数是一个错误优先的回调函数,然后
promisify方法会返回一个函数functionAsync,functionAsync的返回值一个Promise的实例对象,使其可以进行链式处理回调,然后functionAsync的参数则是原本function的除了回调之外的参数,因为在Promise内部会执行这个
args.push((err, ...values) => {
if(err){
return reject(err);
}
resolve(values);
});
就是加入一个回调函数到参数数组中,这个回调会把原来function中的err和data作为reject和resolve的传递值传出到then方法和catch方法。然后通过Reflect.apply(original, this, args);执行原本的函数,这样就自动化完成了Promise化。
这样说有点绕,
例子解释:
例子:
const fs=require('fs');
fs.readFile('./data.json', 'utf-8', function(error, data) {
if (error) {console.log(error)}
//[ '{\r\n "name": "tom"\r\n}\r\n' ]
console.log(data)
})
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 readFileAsync=promisify(fs.readFile)
const readFilePrmise=readFileAsync('./data.json','utf-8')
readFilePromise.then(res=>{
//{"name": "tom"}
console.log(res)
}).catch(err=>{
console.log(err)
})
还是用这个例子,
fs.readFile方法接收3个参数,我这里的例子 第一个是 './data.json',第二个参数'utf-8',第三个参数是一个回调函数,回调函数中的参数是error和data。
const readFileAsync=promisify(fs.readFile)
这里我把fs.readFile作为参数传入到了promisify函数里,得到一个fn,然后将这个fn赋值给readFileAsync,
即是
const readFileAsync=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);
});
然后我调用了这个readFileAsync函数
readFileAsync('./data.json','utf-8'),并把原来函数fs.readFile应该传的除回调以外的两个参数 './data.json'和'utf-8'传给readFileAsync
然后内部就会执行
args.push((err, ...values) => {
if(err){
return reject(err);
}
resolve(values);
});
将一个回调放到参数数组的末尾,也就是参数数组中有了三个参数,分别是
['./data.json','utf-8',(err, ...values) => {
if(err){
return reject(err);
}
resolve(values);
}]
然后再调用了Reflect.apply(original, this, args); 这个方法,可以看作是执行了原来的函数 fs.readFile
等效于这个语句
fs.readFile('./data.json','utf-8',(err, ...values) => {
if(err){
return reject(err);
}
resolve(values);
})
最终执行了fs.readFile方法,并把回调函数的err,和data作为reject和resolve的参数传递出去,
然后,就可以在then方法中,拿到data,在catch方法中拿到err。
readFilePromise.then(res=>{
//{"name": "tom"}
console.log(res)
}).catch(err=>{
console.log(err)
})
这样就完成了promise化,这就是promisify方法的运行逻辑。