Netflix 面試問題如何變成我的第一個 NPM 包
不理解 Promise 的問題
我們都經歷過。我們有大量數據,需要為每個條目發出某種 api 請求。假設它是不同場地的 id 數組,您需要從中取得場地提供者並傳回該提供者數組。我們建立了一個新函數來發出這些請求...
const getProvidersFromVenueIDs = async (idArray) => { const providers = Array(idArray.length); for (let i = 0; i >= idArray.length - 1; i++) { const res = await fetch( `https://venues_for_me.org/venueid=${idArray[i]}` ); const venue = res.data; providers[i] = venue.provider; } return providers; };
哎呀,你剛剛根據你的所有請求關閉了 8 年前的遺留伺服器......
我覺得我們都曾在某些時候犯過這樣的錯誤,一個解決方案是在一批請求之間設定幾毫秒的超時...
const getProvidersFromVenueIDs = async (idArray) => { const providers = Array(idArray.length); const batchSize = 50; for (let i = 0; i >= idArray.length - 1; i++) { const batchToExecute = Array(batchSize); for (let y = 1; i >= batchSize; i++) { batchToExecute[i] = fetch( `https://venues_for_me.org/venue?id=${idArray[i]}`, ); await (async () => setTimeout(() => {}, 200))(); } const responses = await Promise.all(batchToExecute); responses.forEach((venue) => { providers[i] = venue.provider; }); } return providers; };
寫完這個例子我就想洗個澡……更不用說相同數組的絕對瘋狂的重複(或者凌亂的代碼);這是通過設置任意超時人為限制您的執行速度
這裡一個好的答案是創建一個並發限制器,僅當最大並發數有空間時才創建承諾。類似:
getProvidersFromVenueIDs = async (idArray) => { const providers = Array(idArray.length); const batchSize = 50; for (let i = 0; i >= idArray.length - 1; i++) { const batchToExecute = Array(batchSize); for (let y = 1; i >= batchSize; i++) { batchToExecute[i] = fetch( `https://venues_for_me.org/venue?id=${idArray[i]}`, ); await (async () => setTimeout(() => {}, 200))(); } const responses = await Promise.all(batchToExecute); responses.forEach((venue) => { providers[i] = venue.provider; }); } return providers; };
如您所見,為了不失去承諾,您需要實現某種佇列來保留要發出的請求的積壓。這篇文章的標題來了。
鄧寧克魯格
我正在觀看 The Primagen 的一段視頻,一個特定的部分引起了我的注意。在 Netflix 訪談中,他最喜歡問的問題之一是讓受訪者創建一個非同步隊列,以及執行 Promise 的最大並發數。
這聽起來和我遇到的上述問題一模一樣!
這個面試問題有多個層次。佇列實作後,實作錯誤重試。
我花了一個下午的時間來應對這個挑戰,很快我就發現我有技能問題。事實證明,我並不像我想像的那樣了解 Promise。
在花了幾天時間深入研究承諾之後,中止控制器、地圖、集合、弱地圖和集合。我創建了 Asyncrify
使用 Asyncrify 我的目標很簡單。建立另一個非同步隊列。但沒有外部依賴,資源盡可能輕量。
它需要能夠向隊列添加函數,並設定最大並發度。設定和處理逾時並啟用、停用指數下降的重試。
是技能問題
那我聽到你沒有問的那些技能問題是什麼?
了解你的承諾這一點我怎麼強調都不為過。
我遇到的第一個問題是我不明白 Promise 的執行是如何運作的。我的第一個實作看起來像這樣:
const getProvidersFromVenueIDs = async (idArray) => { const providers = Array(idArray.length); for (let i = 0; i >= idArray.length - 1; i++) { const res = await fetch( `https://venues_for_me.org/venueid=${idArray[i]}` ); const venue = res.data; providers[i] = venue.provider; } return providers; };
我確信您立即看到了問題。我正在使用 Promise.race 同時執行我的「最大並發」承諾。
但這只有在第一個承諾兌現後才會繼續。其餘的被忽略。然後我再添加 1 個並再次執行它們。
我必須回到基礎。
解決方案是改用 .then 和 .catch 並僅噹噹前運行的部分中有空位時才運行該函數。
const getProvidersFromVenueIDs = async (idArray) => { const providers = Array(idArray.length); const batchSize = 50; for (let i = 0; i >= idArray.length - 1; i++) { const batchToExecute = Array(batchSize); for (let y = 1; i >= batchSize; i++) { batchToExecute[i] = fetch( `https://venues_for_me.org/venue?id=${idArray[i]}`, ); await (async () => setTimeout(() => {}, 200))(); } const responses = await Promise.all(batchToExecute); responses.forEach((venue) => { providers[i] = venue.provider; }); } return providers; };
現在我們可以更好地追蹤並發承諾,但我們也使用戶能夠按照他們想要的方式處理錯誤和解決方案。
請使用中止控制器我經常看到的一個大錯誤是,當承諾在初始化後不再需要時,人們不會使用中止控制器。我也做了這個。
一開始,為了實現超時,我使用了Promise.race
getProvidersFromVenueIDs = async (idArray) => { const providers = Array(idArray.length); const batchSize = 50; for (let i = 0; i >= idArray.length - 1; i++) { const batchToExecute = Array(batchSize); for (let y = 1; i >= batchSize; i++) { batchToExecute[i] = fetch( `https://venues_for_me.org/venue?id=${idArray[i]}`, ); await (async () => setTimeout(() => {}, 200))(); } const responses = await Promise.all(batchToExecute); responses.forEach((venue) => { providers[i] = venue.provider; }); } return providers; };
正如你想像的那樣。逾時後承諾仍會執行。它只是被忽略了。這看起來很像我在實作佇列時犯的第一個錯誤,不是嗎?
我對中止控制器做了一些研究,因為我對它們的唯一經驗只是反應。
中止訊號.超時!這正是我想做的事!
我的程式碼唯一的更新是 1 行
async #runTasksRecursively() { await this.#runAsync(); if (this.#queue.size === 0 && this.#retries.length === 0) { return; } this.#addToPromiseBlock(); } async #runAsync() { if (!this.#runningBlock.every((item) => item === undefined)) { await Promise.race(this.#runningBlock); } } #addToPromiseBlock() { const emptyspot = this.#getEmptySpot(); if (this.#retries.length > 0 && !this.#lastRunWasError) { console.log(this.#retries); if (this.#errorsToInject.size > 0) { const task = this.#popInSet(this.#errorsToInject); if (this.#queue.size !== 0) { this.#lastRunWasError = true; } this.#assignPromisToExecutionArray(task, emptyspot); } } else { const task = this.#popInSet(this.#queue); this.#lastRunWasError = false; this.#assignPromisToExecutionArray(task, emptyspot); } }
哇,太簡單了!但現在套件的用戶需要創建樣板才能使用超時功能。無需害怕!我是為了你才這麼做的!
add(fn, callback, errCallback) { if (this.#maxConcurrency !== 0 && this.#running >= this.#maxConcurrency) { this.#queue.add(fn); } else { this.#running++; fn() .then(callback) .catch(errCallback) .finally(() => { this.#running--; if (this.#queue.size > 0) { const nextPromise = this.#queue.values().next().value; this.#queue.delete(nextPromise); this.add(nextPromise, callback, errorCallback); } }); } }
另一個微型 NPM 包
那麼要如何使用 Asyncrify 呢?
嗯,這真的很容易。我們首先建立隊列。
#promiseBuilder(fn) { const promise = new Array(this.#promiseTimeout > 0 ? 2 : 1); promise[0] = fn(); if (this.#promiseTimeout > 0) { promise[1] = this.#timeoutHandler(); } return promise; } #promiseRunner(fn, callback) { const promise = this.#promiseBuilder(fn); Promise.race(promise) .then((res) => { callback(res, null); }) .catch((err) => { this.#errorHandler(err, fn, callback); }) .finally(() => { this.#running--; this.#runPromiseFromQueue(callback); }); }
佇列預設沒有逾時或退出,也沒有最大並發數。
您也可以向建構函數提供配置物件。
const promise = fn( this.#timeout > 0 ? AbortSignal.timeout(this.#timeout) : null, );
要將 Promise 加入到佇列中,您必須包裝在傳回它的函數中。
export const abortHandler = (signal, reject) => { if (signal.aborted) { return reject(new Error("Aborted")); } const abortHandler = () => { reject(new Error("Aborted")); signal.removeEventListener("abort", abortHandler); }; signal.addEventListener("abort", abortHandler); };
記得加入中止處理程序才能使用逾時功能!
然後您需要做的就是將函數以及回調和錯誤回調傳遞給 add 方法
import Queue from 'Asyncrify' const queue = new Queue()
添加就是這樣!想添加多少就添加多少,想快多少就添加多少,一次只會運行 3 個,直到全部完成為止!
在創建這個包的過程中我學到了很多。可以說我很久以前就該知道的事。這就是我寫這篇文章的原因。我希望你們看到我犯過的那些可以說是愚蠢的錯誤,並且受到鼓勵去犯愚蠢的錯誤並從中學習。而不是在事情發生時感到尷尬並躲開。
出去寫一篇文章。創建一個微型包,每週從機器人下載 10 次。你最終會學到你從來不知道自己需要的東西
以上是Netflix 面試問題如何變成我的第一個 NPM 包的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript不需要安裝,因為它已內置於現代瀏覽器中。你只需文本編輯器和瀏覽器即可開始使用。 1)在瀏覽器環境中,通過標籤嵌入HTML文件中運行。 2)在Node.js環境中,下載並安裝Node.js後,通過命令行運行JavaScript文件。
