【源码学习】第13期 | 如何从22行源码中get promisify原理?

137 阅读3分钟

前言

    不知不觉国庆假期最后一天了,今天就来浅学一下remote-git-tags这个mini库的源码,主文件代码虽然只有22行,但涉及不少nodejs知识,值得一学~

收获清单

  • 调试源码
  • remote-git-tags原理
  • nodejspromisify 原理

代码准备

  • 下载源码
git clone https://github.com/sindresorhus/remote-git-tags.git
# npm i -g pnpm
cd remote-git-tags 
pnpm install
  • 看package.json开启调试     找到入口文件,打好断点后就可以点击悬浮script标签出现的调试脚本开启调试。从package.json中也可以看出使test命令生效的主要是avaxo,这两个包在我上一篇文章有详细分析,感兴趣的也可以康康,传送门,这里就不展开分析了~

图片.png

  • remote-git-tags用法
        适用场景:获取远程仓库所有tags,可以根据tags列表执行发版操作
// 安装
npm install remote-git-tags
// 使用
import remoteGitTags from 'remote-git-tags';

console.log(await remoteGitTags('https://github.com/sindresorhus/remote-git-tags'));
//=> Map {'v1.0.0' => '69e308412e2a5cffa692951f0274091ef23e0e32', …}

调试分析

调试截图

图片.png

主文件代码分析
// 引入promisify方法
import {promisify} from 'node:util';
// 子进程
import childProcess from 'node:child_process';
// 返回promise
const execFile = promisify(childProcess.execFile);
export default async function remoteGitTags(repoUrl) {
    // 获取远程仓库的tags
    const {stdout} = await execFile('git', ['ls-remote', '--tags', repoUrl]);
    // 定义map
    const tags = new Map();
    // 遍历tags,把所有tag及对应hash值放进map里
    for (const line of stdout.trim().split('\n')) {

        const [hash, tagReference] = line.split('\t');

        // Strip off the indicator of dereferenced tags so we can override the

        // previous entry which points at the tag hash and not the commit hash

        // `refs/tags/v9.6.0^{}` → `v9.6.0`

        const tagName = tagReference.replace(/^refs\/tags\//, '').replace(/\^{}$/, '');

        tags.set(tagName, hash);

    }

    return tags;

}
child_process.execFile()

    提到child_process.execFile()就很有必要提一下child_process.exec(),下面是两者对比

  • child_process.exec():生成一个shell并在该shell中运行一个命令,执行完成时传递stdoutstderr给回调函数。

  • child_process.execFile():类似child_process.exec(),区别在它在没有首先生成一个shell而是直接生成命令。

nodejs如何标识核心模块、加载模块?

    可以用node:前缀标识nodejs核心模块,这个也可以在官网的Core modules找到解析,传送门

图片.png

    从remote-git-tags源码的packaage.json中的type可以看出这里是以es6模块加载js文件,这也是为啥源码中可以以import形式引入promisify,如果不设置则默认为commonjs模块,就要改用require的方式引入 图片.png

promisify

    还是看回源码,有这么一行应用promisify的代码

const execFile = promisify(childProcess.execFile);

    点击源文件,可以看到以下代码,由此可见,这里主要是利用函数重载将函数的callback形式转换为promise形式,在我们日常编写工具函数时也可以借鉴这种写法,有利于功能拓展的同时结构单一并能根据入参各司其职执行相应的处理 图片.png

总结

    今天调试分析了remote-git-tags源码,深入了解了一下node加载模块以及标识核心模块,回顾了nodejs子进程运行命令的差异,最后粗略分析了一下promisify的原理,又在“点点点”中结束了一个源码包的学习😁!

参考文章

从22行有趣的源码库中,我学到了 callback promisify 化的 Node.js 源码实现