每天阅读一个 npm 模块(6)- pify

栏目: Node.js · 发布时间: 6年前

内容简介:系列文章:之前阅读的 npm 模块都来源于今天阅读的模块是pify,通过它可以将很多采用 callback 方式进行调用的函数变成 Promise 调用,甚至采用 async/await 语法进行异步调用,从而可以在不修改调用函数的情况下避免回调地狱,也可以让代码具有更好的可读性,当前的版本是 4.0.0,周下载量约为 750 万。

系列文章:

  1. 每天阅读一个 npm 模块(1)- username
  2. 每天阅读一个 npm 模块(2)- mem
  3. 每天阅读一个 npm 模块(3)- mimic-fn
  4. 每天阅读一个 npm 模块(4)- throttle-debounce
  5. 每天阅读一个 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 这个函数的具体实现。这个函数也十分简单,主要做了四件事情:

  1. 构造一个 Promise 并将其作为函数的返回值。
  2. 构造一个 callback 函数,在这个函数中,假如有错误,则调用 Promise.reject() 方法抛出异常;假如无错误,则调用 Promise.resolve() 返回正常结果。
  3. 对于传入的参数 args 通过 push 方法追加我们刚刚构造的 callback 函数,从而形成完整的参数。
  4. 最后通过 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 ,主要为两点:

  1. pify 支持 Node.js 6.0 及以上版本, util.promisify(fn) 只支持 Node.js 8.0 及以上版本。
  2. pify 支持对整个模块 Promise 化, util.promisify(fn) 只支持对单个函数的 Promise 化。

关于我:毕业于华科,工作在腾讯,elvin 的博客 欢迎来访 ^_^


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

解构产品经理:互联网产品策划入门宝典

解构产品经理:互联网产品策划入门宝典

电子工业出版社 / 2018-1 / 65

《解构产品经理:互联网产品策划入门宝典》以作者丰富的职业背景及著名互联网公司的工作经验为基础,从基本概念、方法论和工具的解构入手,配合大量正面或负面的案例,完整、详细、生动地讲述了一个互联网产品经理入门所需的基础知识。同时,在此基础上,将这些知识拓展出互联网产品策划的领域,融入日常工作生活中,以求职、沟通等场景为例,引导读者将知识升华为思维方式。 《解构产品经理:互联网产品策划入门宝典》适合......一起来看看 《解构产品经理:互联网产品策划入门宝典》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

SHA 加密
SHA 加密

SHA 加密工具