面試官:請實現一個通用函數把 callback 轉成 promise

1. 前言

大家好,我是若川。最近組織了源碼共讀活動,感興趣的可以加我微信 ruochuan12 參與,或者在公眾號:若川視野,回復"源碼"參與,每周大家一起學習200行左右的源碼,共同進步。已進行三個月了,很多小伙伴表示收獲頗豐。

想學源碼,極力推薦之前我寫的《學習源碼整體架構系列》 包含jQueryunderscorelodashvuexsentryaxiosreduxkoavue-devtoolsvuex4koa-composevue-next-releasevue-thiscreate-vue玩具vite等10余篇源碼文章。

本文倉庫 remote-git-tags-analysis,求個star^_^[1]

我們經常會在本地git倉庫切換tags,或者git倉庫切換tags。那么我們是否想過如果獲取tags呢。本文就是學習 remote-git-tags 這個22行代碼的源碼庫。源碼不多,但非常值得我們學習。

閱讀本文,你將學到:

1.?Node?加載采用什么模塊
2.?獲取?git?倉庫所有?tags?的原理
3.?學會調試看源碼
4.?學會面試高頻考點?promisify?的原理和實現
5.?等等

剛開始先不急著看上千行、上萬行的源碼。源碼長度越長越不容易堅持下來。看源碼講究循序漸進。比如先從自己會用上的百來行的開始看。

我之前在知乎上回答過類似問題。

一年內的前端看不懂前端框架源碼怎么辦?

簡而言之,看源碼

循序漸進
借助調試
理清主線
查閱資料
總結記錄

2. 使用

import?remoteGitTags?from?'remote-git-tags';console.log(await?remoteGitTags('https://github.com/lxchuan12/blog.git'));
//=>?Map?{'3.0.5'?=>?'6020cc35c027e4300d70ef43a3873c8f15d1eeb2',?…}

3. 源碼

Get tags from a remote Git repo

這個庫的作用是:從遠程倉庫獲取所有標簽。

原理:通過執行 git ls-remote --tags repoUrl (倉庫路徑)獲取 tags

應用場景:可以看有哪些包依賴的這個包。npm 包描述信息[2]

其中一個比較熟悉的是npm-check-updates[3]

npm-check-updates 將您的 package.json 依賴項升級到最新版本,忽略指定的版本。

還有場景可能是 github 中獲取所有 tags 信息,切換 tags 或者選定 tags 發布版本等,比如微信小程序版本。

看源碼前先看 package.json 文件。

3.1 package.json

//?package.json
{//?指定?Node?以什么模塊加載,缺省時默認是?commonjs"type":?"module","exports":?"./index.js",//?指定?nodejs?的版本"engines":?{"node":?"^12.20.0?||?^14.13.1?||?>=16.0.0"},"scripts":?{"test":?"xo?&&?ava"}
}

眾所周知,Node 之前一直是 CommonJS 模塊機制。Node 13 添加了對標準 ES6 模塊的支持。

告訴 Node 它要加載的是什么模塊的最簡單的方式,就是將信息編碼到不同的擴展名中。如果是 .mjs 結尾的文件,則 Node 始終會將它作為 ES6 模塊來加載。如果是 .cjs 結尾的文件,則 Node 始終會將它作為 CommonJS 模塊來加載。

對于以 .js 結尾的文件,默認是 CommonJS 模塊。如果同級目錄及所有目錄有 package.json 文件,且 type 屬性為module 則使用 ES6 模塊。type 值為 commonjs 或者為空或者沒有 package.json 文件,都是默認 commonjs 模塊加載。

關于 Node 模塊加載方式,在《JavaScript權威指南第7版》16.1.4 Node 模塊 小節,有更加詳細的講述。此書第16章都是講述Node,感興趣的讀者可以進行查閱。

3.2 調試源碼

#?推薦克隆我的項目,保證與文章同步,同時測試文件齊全
git?clone?https://github.com/lxchuan12/remote-git-tags-analysis.git
#?npm?i?-g?yarn
cd?remote-git-tags?&&?yarn
#?VSCode?直接打開當前項目
#?code?.#?或者克隆官方項目
git?clone?https://github.com/sindresorhus/remote-git-tags.git
#?npm?i?-g?yarn
cd?remote-git-tags?&&?yarn
#?VSCode?直接打開當前項目
#?code?.

用最新的VSCode 打開項目,找到 package.jsonscripts 屬性中的 test 命令。鼠標停留在test命令上,會出現 運行命令調試命令 的選項,選擇 調試命令 即可。

調試如圖所示:

a33ae89f11eff8866b1b246d85f7783e.png
調試如圖所示

VSCode 調試 Node.js 說明如下圖所示:

ffe8b69c592d0438388740f021da55d5.png
VSCode 調試 Node.js 說明

更多調試詳細信息,可以查看這篇文章新手向:前端程序員必學基本技能——調試JS代碼。

跟著調試,我們來看主文件。

3.3 主文件僅有22行源碼

//?index.js
import?{promisify}?from?'node:util';
import?childProcess?from?'node:child_process';const?execFile?=?promisify(childProcess.execFile);export?default?async?function?remoteGitTags(repoUrl)?{const?{stdout}?=?await?execFile('git',?['ls-remote',?'--tags',?repoUrl]);const?tags?=?new?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;
}

源碼其實一眼看下來就很容易懂。

3.4 git ls-remote --tags

支持遠程倉庫鏈接。

git ls-remote 文檔[4]

如下圖所示:

048a524389d46f2e9841f88eb4fc66e0.png
ls-remote

獲取所有tags git ls-remote --tags https://github.com/vuejs/vue-next.git

把所有 tags 和對應的 hash值 存在 Map 對象中。

3.5 node:util

Node 文檔[5]

Core modules can also be identified using the node: prefix, in which case it bypasses the require cache. For instance, require('node:http') will always return the built in HTTP module, even if there is require.cache entry by that name.

也就是說引用 node 原生庫可以加 node: 前綴,比如 import util from 'node:util'

看到這,其實原理就明白了。畢竟只有22行代碼。接著講述 promisify

4. promisify

源碼中有一段:

const?execFile?=?promisify(childProcess.execFile);

promisify 可能有的讀者不是很了解。

接下來重點講述下這個函數的實現。

promisify函數是把 callback 形式轉成 promise 形式。

我們知道 Node.js 天生異步,錯誤回調的形式書寫代碼。回調函數的第一個參數是錯誤信息。也就是錯誤優先。

我們換個簡單的場景來看。

4.1 簡單實現

假設我們有個用JS加載圖片的需求。我們從 這個網站[6] 找來圖片。

examples
const?imageSrc?=?'https://www.themealdb.com/images/ingredients/Lime.png';function?loadImage(src,?callback)?{const?image?=?document.createElement('img');image.src?=?src;image.alt?=?'公眾號若川視野專用圖?';image.style?=?'width:?200px;height:?200px';image.onload?=?()?=>?callback(null,?image);image.onerror?=?()?=>?callback(new?Error('加載失敗'));document.body.append(image);
}

我們很容易寫出上面的代碼,也很容易寫出回調函數的代碼。需求搞定。

loadImage(imageSrc,?function(err,?content){if(err){console.log(err);return;}console.log(content);
});

但是回調函數有回調地獄等問題,我們接著用 promise 來優化下。

4.2 promise 初步優化

我們也很容易寫出如下代碼實現。

const?loadImagePromise?=?function(src){return?new?Promise(function(resolve,?reject){loadImage(src,?function?(err,?image)?{if(err){reject(err);return;}resolve(image);});});
};
loadImagePromise(imageSrc).then(res?=>?{console.log(res);
})
.catch(err?=>?{console.log(err);
});

但這個不通用。我們需要封裝一個比較通用的 promisify 函數。

4.3 通用 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;
}const?loadImagePromise?=?promisify(loadImage);
async?function?load(){try{const?res?=?await?loadImagePromise(imageSrc);console.log(res);}catch(err){console.log(err);}
}
load();

需求搞定。這時就比較通用了。

這些例子在我的倉庫存放在 examples 文件夾中。可以克隆下來,npx http-server .跑服務,運行試試。

2d6b19a03d1b3858d2b49ac8696471a1.png
examples

跑失敗的結果可以把 imageSrc 改成不存在的圖片即可。

promisify 可以說是面試高頻考點。很多面試官喜歡考此題。

接著我們來看 Node.js 源碼中 promisify 的實現。

4.4 Node utils promisify 源碼

github1s node utils 源碼[7]

源碼就暫時不做過多解釋,可以查閱文檔。結合前面的例子,其實也容易理解。

utils promisify 文檔[8]

const?kCustomPromisifiedSymbol?=?SymbolFor('nodejs.util.promisify.custom');
const?kCustomPromisifyArgsSymbol?=?Symbol('customPromisifyArgs');let?validateFunction;function?promisify(original)?{//?Lazy-load?to?avoid?a?circular?dependency.if?(validateFunction?===?undefined)({?validateFunction?}?=?require('internal/validators'));validateFunction(original,?'original');if?(original[kCustomPromisifiedSymbol])?{const?fn?=?original[kCustomPromisifiedSymbol];validateFunction(fn,?'util.promisify.custom');return?ObjectDefineProperty(fn,?kCustomPromisifiedSymbol,?{value:?fn,?enumerable:?false,?writable:?false,?configurable:?true});}//?Names?to?create?an?object?from?in?case?the?callback?receives?multiple//?arguments,?e.g.?['bytesRead',?'buffer']?for?fs.read.const?argumentNames?=?original[kCustomPromisifyArgsSymbol];function?fn(...args)?{return?new?Promise((resolve,?reject)?=>?{ArrayPrototypePush(args,?(err,?...values)?=>?{if?(err)?{return?reject(err);}if?(argumentNames?!==?undefined?&&?values.length?>?1)?{const?obj?=?{};for?(let?i?=?0;?i?<?argumentNames.length;?i++)obj[argumentNames[i]]?=?values[i];resolve(obj);}?else?{resolve(values[0]);}});ReflectApply(original,?this,?args);});}ObjectSetPrototypeOf(fn,?ObjectGetPrototypeOf(original));ObjectDefineProperty(fn,?kCustomPromisifiedSymbol,?{value:?fn,?enumerable:?false,?writable:?false,?configurable:?true});return?ObjectDefineProperties(fn,ObjectGetOwnPropertyDescriptors(original));
}promisify.custom?=?kCustomPromisifiedSymbol;

5. ES6+ 等知識

文中涉及到了Mapfor of、正則、解構賦值。

還有涉及封裝的 ReflectApplyObjectSetPrototypeOfObjectDefinePropertyObjectGetOwnPropertyDescriptors 等函數都是基礎知識。

這些知識可以查看esma規范[9],或者阮一峰老師的《ES6 入門教程》[10] 等書籍。

6. 總結

一句話簡述 remote-git-tags 原理:使用Node.js的子進程 child_process 模塊的execFile方法執行 git ls-remote --tags repoUrl 獲取所有 tagstags 對應 hash 值 存放在 Map 對象中。

文中講述了我們可以循序漸進,借助調試、理清主線、查閱資料、總結記錄的流程看源碼。

通過 remote-git-tags 這個22行代碼的倉庫,學會了 Node 加載采用什么模塊,知道了原來 git ls-remote --tags支持遠程倉庫,學到了面試高頻考點 promisify 函數原理和源碼實現,鞏固了一些 ES6+ 等基礎知識。

建議讀者克隆我的倉庫[11]動手實踐調試源碼學習。

后續也可以看看 es6-promisify[12] 這個庫的實現。

最后可以持續關注我@若川。歡迎加我微信 ruochuan12 交流,參與 源碼共讀 活動,大家一起學習源碼,共同進步。

參考資料

[1]

本文倉庫 remote-git-tags-analysis,求個star^_^: https://github.com/lxchuan12/remote-git-tags-analysis.git

[2]

npm 包描述信息: https://npm.im/remote-git-tags

[3]

npm-check-updates: https://www.npmjs.com/package/npm-check-updates

[4]

git ls-remote 文檔: https://git-scm.com/docs/git-ls-remote

[5]

Node 文檔: https://nodejs.org/dist/latest-v16.x/docs/api/modules.html

[6]

這個網站: https://www.themealdb.com/api.php

[7]

github1s node utils 源碼: https://github1s.com/nodejs/node/blob/master/lib/internal/util.js#L343

[8]

utils promisify 文檔: http://nodejs.cn/api/util/util_promisify_original.html

[9]

esma規范: https://yanhaijing.com/es5/

[10]

《ES6 入門教程》: https://es6.ruanyifeng.com/

[11]

我的倉庫: https://github.com/lxchuan12/remote-git-tags-analysis.git

[12]

es6-promisify: https://github.com/mikehall314/es6-promisify

最近組建了一個江西人的前端交流群,如果你是江西人可以加我微信?ruochuan12?私信 江西 拉你進群。

推薦閱讀

1個月,200+人,一起讀了4周源碼
我歷時3年才寫了10余篇源碼文章,但收獲了100w+閱讀

老姚淺談:怎么學JavaScript?

我在阿里招前端,該怎么幫你(可進面試群)

a83325eae61f0693f903ea78a713ff5d.gif

·················?若川簡介?·················

你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》10余篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。
同時,最近組織了源碼共讀活動,幫助1000+前端人學會看源碼。公眾號愿景:幫助5年內前端人走向前列。

6cf43d70cc457039869c5d44ad1ae612.png

識別方二維碼加我微信、拉你進源碼共讀

今日話題

略。歡迎分享、收藏、點贊、在看我的公眾號文章~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/275225.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/275225.shtml
英文地址,請注明出處:http://en.pswp.cn/news/275225.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java中filter的用法

filter過濾器主要使用于前臺向后臺傳遞數據是的過濾操作。程度很簡單就不說明了&#xff0c;直接給幾個已經寫好的代碼&#xff1a; 一、使瀏覽器不緩存頁面的過濾器 Java代碼 import javax.servlet.*;import javax.servlet.http.HttpServletResponse;import java.io.IOExcept…

我很喜歡玩游戲,那么我就適合做游戲程序員嗎?

作者&#xff1a;黃小斜文章來源&#xff1a;【程序員江湖】游戲在今天的普及度已經不是端游時代可以比肩的了。如今人手一臺手機、平板就可以吃雞、打農藥&#xff0c;不僅是男生&#xff0c;也有很多女生加入了游戲圈。相信現在在看文章的你也玩游戲&#xff0c;雖然愛玩的程…

open-falcon_NASA在Falcon 9上帶回了蠕蟲-其背后的故事是什么?

open-falconYes, that’s right. The classic NASA “worm” logo is back! An image of the revived NASA worm logo was released on Twitter by NASA Administrator Jim Bridenstine as well as press release on the NASA.gov website. NASA explained that original NASA …

聽說你對 ES6 class 類還不是很了解

大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12 參與。前言在ES5中是原型函數&#xff0c;到了ES6中出現了"類"的概念。等同于是ES5的語法糖&#xff0c;大大提升了編寫代碼的速度&#xff0c;本文只講一些常用的&…

《CSS揭秘》讀書筆記

摘要 《CSS揭秘》主要是介紹了使用CSS的技巧&#xff0c;通過47個案例來靈活的使用CSS進行實現&#xff0c;同時在實現過程中注重CSS代碼的靈活性與健壯性。通過閱讀這本書有利于我們編寫高質量的CSS代碼以及打破使用CSS時的固定思維&#xff0c;能夠更加靈活的使用CSS。 《CSS…

一篇文章帶你搞懂前端面試技巧及進階路線

大家好&#xff0c;我是若川。最近有很多朋友給我后臺留言&#xff1a;自己投了不少簡歷&#xff0c;但是收到的面試邀請卻特別少&#xff1b;好不容易收到了大廠的面試邀請&#xff0c;但由于對面試流程不清楚&#xff0c;準備的特別不充分&#xff0c;結果也掛了&#xff1b;…

小屏幕 ui設計_UI設計基礎:屏幕

小屏幕 ui設計重點 (Top highlight)第4部分 (Part 4) Welcome to the fourth part of the UI Design basics. This time we’ll cover the screens you’ll likely design for. This is also a part of the free chapters from Designing User Interfaces.歡迎使用UI設計基礎知…

RabbitMQ指南之四:路由(Routing)和直連交換機(Direct Exchange)

在上一章中&#xff0c;我們構建了一個簡單的日志系統&#xff0c;我們可以把消息廣播給很多的消費者。在本章中我們將增加一個特性&#xff1a;我們可以訂閱這些信息中的一些信息。例如&#xff0c;我們希望只將error級別的錯誤存儲到硬盤中&#xff0c;同時可以將所有級別&am…

不用任何插件實現 WordPress 的彩色標簽云

側邊欄的標簽云&#xff08;Tag Cloud&#xff09;一直是 WordPress 2.3 以后的內置功能&#xff0c;一般直接調用函數wp_tag_cloud 或者在 Widgets 里開啟即可&#xff0c;但是默認的全部是一個顏色&#xff0c;只是大小不一樣&#xff0c;很是不順眼&#xff0c;雖然可以用 S…

隨時隨地能寫代碼, vscode.dev 出手了

大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12 參與。今天偶然看到了 VSCode 官方發布了一條激動人心的 Twitter&#xff0c;vscode.dev[1] 域名上線了&#xff01;image-20211021211915942新的域名 vscode.dev[2] 它是一個…

七種主流設計風格_您是哪種設計風格?

七種主流設計風格重點 (Top highlight)I had an idea for another mindblowing test, so here it is. Since you guys liked the first one so much, and I got so many nice, funny responses and private messages on how accurate it actually was, I thought you will prob…

算法精講:分享一道值得分享的算法題

分享一道leetcode上的題&#xff0c;當然&#xff0c;居然不是放在刷題貼里來講&#xff0c;意味著分享的這道題不僅僅是教你怎么來解決&#xff0c;更重要的是這道題引發出來的一些解題技巧或許可以用在其他地方&#xff0c;下面我們來看看這道題的描述。 問題描述 給定一個未…

正幾邊形可以實現無縫拼接?

正n邊形內角為 (n-2)*180/n &#xff0c;要保證可以無縫拼接&#xff0c;就是一個圓可以被整數個n邊形內角拼接&#xff0c;即 360k*(n-2)*180/n > 2nk(n-2)。&#xff08;摘自http://blog.csdn.net/ray58750034/article/details/1365813&#xff09; 以下代碼表明&#xff…

React 18 Beta 來了

大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12 參與&#xff0c;目前近3000人參與。經過「React18工作組」幾個月工作&#xff0c;11月16日v18終于從Alpha版本更新到Beta版本。本文會解釋&#xff1a;這次更新帶來的變化對開…

osg著色語言著色_探索數字著色

osg著色語言著色Learn how to colorize icons with your NounPro subscription and Adobe Illustrator.了解如何使用NounPro訂閱和Adobe Illustrator為圖標著色。 For those who want to level up their black and white Noun Project icons with a splash of color, unlockin…

upc組隊賽15 Supreme Number【打表】

Supreme Number題目鏈接 題目描述 A prime number (or a prime) is a natural number greater than 1 that cannot be formed by multiplying two smaller natural numbers. Now lets define a number N as the supreme number if and only if each number made up of an non-e…

CSS3實踐之路(一):CSS3之我觀

CSS 的英文全稱Cascading Style Sheets&#xff0c;中文意思是級聯樣式表,通過設立樣式表&#xff0c;可以統一地控制HMTL中各DOM元素的顯示屬性。級聯樣式表可以使人更能有效地控制網頁外觀。使用級聯樣式表&#xff0c;可以擴充精確指定網頁元素位置&#xff0c;外觀以及創建…

18個項目必備的JavaScript代碼片段——數組篇

大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12 參與&#xff0c;目前近3000人參與&#xff0c;0-5年工作經驗的都可以參與學習。1.chunk轉換二維數組將數組&#xff08;array&#xff09;拆分成多個數組&#xff0c;并將這些…

美學評價_卡美學的真正美

美學評價In collectible card games like Hearthstone, Legends of Runeterra, and Magic: The Gathering, the aesthetic of the cards is indubitably one of the greatest highlights for many, if not all players. Although the game loop is reliant on physically build…

好程序員web前端分享CSS Bug、CSS Hack和Filter學習筆記

為什么80%的碼農都做不了架構師&#xff1f;>>> CSS Bug、CSS Hack和Filter學習筆記 1)CSS Bug:CSS樣式在各瀏覽器中解析不一致的情況&#xff0c;或者說CSS樣式在瀏覽器中不能正確顯示的問題稱為CSS bug. 2)CSS Hack: CSS中&#xff0c;Hack是指一種兼容CSS在不同…