内容简介:系列文章:之前阅读的 npm 模块都来源于今天阅读的模块是pify,通过它可以将很多采用 callback 方式进行调用的函数变成 Promise 调用,甚至采用 async/await 语法进行异步调用,从而可以在不修改调用函数的情况下避免回调地狱,也可以让代码具有更好的可读性,当前的版本是 4.0.0,周下载量约为 750 万。
系列文章:
- 每天阅读一个 npm 模块(1)- username
- 每天阅读一个 npm 模块(2)- mem
- 每天阅读一个 npm 模块(3)- mimic-fn
- 每天阅读一个 npm 模块(4)- throttle-debounce
- 每天阅读一个 npm 模块(5)- ee-first
之前阅读的 npm 模块都来源于 awesome-micro-npm-packages 这个项目,不过浏览了一些之后,发现好多都不太适合拿来做源码学习。如果读者有推荐的适合的模块,欢迎在评论区指出 :blush:
一句话介绍
今天阅读的模块是pify,通过它可以将很多采用 callback 方式进行调用的函数变成 Promise 调用,甚至采用 async/await 语法进行异步调用,从而可以在不修改调用函数的情况下避免回调地狱,也可以让代码具有更好的可读性,当前的版本是 4.0.0,周下载量约为 750 万。
用法
以 Node.js 中异步读取文件为例,常用的方法之一就是 fs.readFile(path, encoding, callback)
,这种通过回调函数进行异步操作的方式在以前的代码中十分常见 ,也是迫不得已。但是当如今拥有了 Promise 之后,这样写就显得十分麻烦,也不易于维护,所以可以通过 pify
这个模块将他们 Promise 化(即 Promisify)。
const fs = require('fs'); const pify = require('pify'); // 将 fs.readFile 变成 Promise 调用 pify(fs.readFile)('package.json', 'utf8').then(data => { console.log(JSON.parse(data).name); // => 'pify' }); // 通过 Promise 化函数,使用 async/await 语法 (async function(){ const data = await pify(fs.readFile)('package.json', 'utf-8'); console.log(JSON.parse(data)); // => 'pify' })(); 复制代码
除了直接对一个函数进行 Promise 化外,还可以对一整个模块中的每一个函数进行 Promise 化:
const fs = require('fs'); const pify = require('pify'); // 将 fs 模块 Promise 化 pify(fs).readFile('package.json', 'utf8').then(data => { console.log(JSON.parse(data).name); // => 'pify' }); 复制代码
源码学习
函数 Promise 化
// 源码 6-1 module.exports = (input) => { let ret; if (typeof input === 'function') { ret = (...args) => processFn(input)(...args); } return ret; } 复制代码
pify
主函数入口十分简单,如果传入的参数为函数,则经过 processFn
处理后作为结果返回,这里两个 ...args
虽然看起来一样,但实际上是 ES6新增的不同语法:
-
第一个
...args
用法叫做函数 rest 参数,可以用来获取函数的多余参数。它不同于arguments
是一个类数组的类型,而是一个数组的实例:function foo(name, ...rest) { console.log(rest, rest instanceof Array); } foo('Elvin', 'likes', 'JavaScript'); // => [ 'likes', 'JavaScript' ], true 复制代码
-
第二个
...args
的用法叫做扩展运算符(spread),类似于 rest 参数的逆运算,将一个数组进行展开:const x = [1, 2, 3]; const y = [...x, 4]; console.log(...x); // => 1 2 3 console.log(y); // =>[ 1, 2, 3, 4 ] 复制代码
这里实际上没有必要进行一层包裹,可以直接返回 processFn
处理的函数,即变成 ret = processFn(input)
,我也根据这个想法提出了 pify - PR#65
。
接下来看一看 processFn
这个函数的具体实现。这个函数也十分简单,主要做了四件事情:
- 构造一个 Promise 并将其作为函数的返回值。
-
构造一个 callback 函数,在这个函数中,假如有错误,则调用
Promise.reject()
方法抛出异常;假如无错误,则调用Promise.resolve()
返回正常结果。 -
对于传入的参数
args
通过 push 方法追加我们刚刚构造的 callback 函数,从而形成完整的参数。 -
最后通过
fn.apply(this, args)
调用原函数。
// 源码 6-2 const processFn = (fn) => function (...args) { return new Promise((resolve, reject) => { args.push((error, result) => { if (error) { reject(error); } else { resolve(result); } }); fn.apply(this, args); }); }; 复制代码
对象 Promise 化
对象的 Promise 化其实就是遍历对象的每一个属性,如果属性类型为函数的话,那么就用上节所说的 processFn
进行处理;如果属性类型不为函数的话,则直接返回:
// 源码 6-3 module.exports = (input) => { for (const key in input) { const property = input[key]; ret[key] = typeof property === 'function' ? processFn(property) : property; } } 复制代码
写在最后
今天阅读的pify 模块的代码其实不难,但是它的的确确解决了开发过程中的痛点,所以它能在 Github -pify 上获得 1000+ 的赞,在 npm 上每周的下载量高达 750 万。
另外从 Node.js 8.0 起,就内置了 util.promisify(fn)
方法,可以实现部分pify 的功能,官方文档可以参考 Node.js - util.promisify
,关于两者的区别可以参考 How does this differ from util.promisfy
,主要为两点:
-
pify
支持 Node.js 6.0 及以上版本,util.promisify(fn)
只支持 Node.js 8.0 及以上版本。 -
pify
支持对整个模块 Promise 化,util.promisify(fn)
只支持对单个函数的 Promise 化。
关于我:毕业于华科,工作在腾讯,elvin 的博客 欢迎来访 ^_^
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Nginx源码阅读笔记-事件处理模块
- 每天阅读一个 npm 模块(2)- mem
- 每天阅读一个 npm 模块(1)- username
- # 每天阅读一个 npm 模块(7)- delegates
- 《WebKit技术内幕》阅读摘要 —— WebKit 架构和模块
- 每天阅读一个 npm 模块(3)- mimic-fn
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。