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

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

内容简介:系列文章:之前阅读的 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 的博客 欢迎来访 ^_^


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

查看所有标签

猜你喜欢:

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

Practical Django Projects, Second Edition

Practical Django Projects, Second Edition

James Bennett / Apress / 2009 / 44.99

Build a django content management system, blog, and social networking site with James Bennett as he introduces version 1.1 of the popular Django framework. You’ll work through the development of ea......一起来看看 《Practical Django Projects, Second Edition》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

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

Base64 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具