JavaScript實現(xiàn)一個Promise隊列小工具
摘要
在百度的解釋中,隊列是一種特殊的線性表,特殊之處在于它只允許在表的前端進(jìn)行刪除操作,而在表的后端進(jìn)行插入操作,和棧一樣,隊列是一種操作受限制的線性表。進(jìn)行插入操作的端稱為隊尾,進(jìn)行刪除操作的端稱為隊頭。
受這個隊列結(jié)構(gòu)的啟發(fā),在前端不同的業(yè)務(wù)場景中,由于一次性發(fā)起的異步請求過多,并且這些服務(wù)位于不同組件或者不同兄弟功能之間,我們無法直接對這些方法進(jìn)行同步,所以需要引入隊列的概念,對這些并發(fā)性的問題進(jìn)行順序處理。
思考
為什么要寫一個類似于隊列的功能?
我們知道,在主流的V8引擎中,JavaScript
的執(zhí)行順序都是至上而下的,引入了 ajax
之后我們能夠?qū)W(wǎng)頁進(jìn)行局部刷新,在前端領(lǐng)域第一次有了異步操作的概念。自此,當(dāng)我們接觸 Promise
之后也不在陌生于這種模式。當(dāng)然,使用過 Promise
的都知道,如果是在某個單一的 js
文件中,我們完全可以通過編寫一個 async
函數(shù),通過使用 await
去實現(xiàn)將 Promise
函數(shù)按順序執(zhí)行的功能。
那這個隊列小工具難道就是無意義的嗎?作者就像一個大渣男,玩弄了一番感情輕飄飄的走了?當(dāng)然不是,大家都知道在單一文件中,我們總有辦法去實現(xiàn)這樣控制異步操作的功能,那么在不同的模塊、組件中,如何去實現(xiàn)這樣的操作呢?按照這個思路,我們可以發(fā)現(xiàn)單純的去寫是沒辦法直接解決這樣的異步功能同時觸發(fā)的問題的,這也就是作者為什么要去些一個類似于隊列的功能。
Promise
隊列功能的思路在哪里?
我們先要搞明白我們想要的是一個怎樣的功能呢?作者畫了一個大概的需要實現(xiàn)功能的流程圖,我們不同組件的異步請求是同時發(fā)生的,我們需要在發(fā)生之前把Promise
函數(shù)放入隊列工具,再讓隊列工具去調(diào)度出棧和入棧的順序,每次出棧的時候再去調(diào)用 Promise
函數(shù)發(fā)生請求。
我們可以在 js
文件中使用一個單例的概覽,主要是記錄入棧的順序和對操作加鎖,并且啟動一個定時服務(wù)去定期檢測棧中是否還有順序,思路是很簡單的,說干就干。
實現(xiàn)
我們新建一個 useQueue.js
文件,為了方便后續(xù)的引用。首先我們需要定義三個高級變量:
var queue = [] var locks = false var timer = null
queue
:記錄入棧的Promise
任務(wù)函數(shù)locks
:在處理出棧任務(wù)時我們需要對函數(shù)加鎖,防止多次出棧timer
:防抖記錄,跟功能locks
主要起一個保險左右,防止出棧時重復(fù)調(diào)用函數(shù)
我們定義兩個函數(shù) hander
、useQueue
,導(dǎo)出默認(rèn)函數(shù) useQueue
。
/** * 處理任務(wù)出棧 */ const hander = () => {} /** * 入棧等待排隊操作 * @param {Promise} task */ const useQueue = (task) => {} /** * 導(dǎo)出 */ export default useQueue
當(dāng)我們有任務(wù)進(jìn)入時,啟用 useQueue
函數(shù),將任務(wù)放入棧中,并且設(shè)置 locks = true
進(jìn)行解鎖。
const useQueue = (task) => { queue.push(task) locks = true }
這個時候,我們在單例上設(shè)置一個心跳來定期檢測加鎖狀態(tài),如果是解鎖則觸發(fā)處理函數(shù)。
// 心跳 setInterval(() => { locks && handler() }, 100)
當(dāng)函數(shù) hander
被成果觸發(fā),我們需要設(shè)置一個延時器來處理防抖。先進(jìn)行加鎖,然后在隊列中進(jìn)行出棧,如果出棧為空則解鎖;如果出棧不為空則調(diào)用任務(wù),并且等待任務(wù)結(jié)束進(jìn)入下一個 hander
。
const handler = () => { timer && clearTimeout(timer) timer = setTimeout(() => { locks = false const task = queue.shift() if (task) { task().then(handler) } else { locks = true } }) }
假設(shè)我們已經(jīng)寫了一個 vue
字典標(biāo)簽組件,組件內(nèi)部會調(diào)用遠(yuǎn)程接口,并且再記錄到某一個緩存中。如果一個頁面多次使用組件,而該緩存還并未存在,那么可能會導(dǎo)致同時觸發(fā)多個異步網(wǎng)絡(luò)請求。這個時候就可以使用隊列工具。
import useQueue from './useQueue.js' const handleAjax = () => { return new Promise(resolve => { ... resolve() }) } useQueue(handleAjax) // 或者可以這樣調(diào)用 useQueue(async () => { await handleAjax() })
總結(jié)
Promise
隊列工具實際上是通過延遲觸發(fā),來處理掛載在某一隊列數(shù)據(jù)上的出棧任務(wù),原理其實并不復(fù)雜,就是為了處理異步請求同時發(fā)生的情況。當(dāng)然,這個函數(shù)可以有更多的優(yōu)化方案,比如說可以添加一個優(yōu)先級的參數(shù)值,根據(jù)不同優(yōu)先級進(jìn)行入棧操作,或者增加一個同類型排異等等。
到此這篇關(guān)于JavaScript實現(xiàn)一個Promise隊列小工具的文章就介紹到這了,更多相關(guān)JS Promise隊列內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Js原型鏈constructor prototype __proto__屬性實例詳解
這篇文章主要介紹了Js原型鏈constructor prototype __proto__屬性實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10Lodash加減乘除add?subtract?multiply?divide方法源碼解讀
這篇文章主要介紹了Lodash加減乘除add?subtract?multiply?divide方法源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05JavaScript設(shè)計模式之命令模式和狀態(tài)模式詳解
這篇文章主要為大家介紹了JavaScript設(shè)計模式之命令模式和狀態(tài)模式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08JavaScript獲取上傳文件相關(guān)信息示例詳解
這篇文章主要為大家介紹了JavaScript獲取上傳文件相關(guān)信息示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08JavaScript實現(xiàn)余額數(shù)字滾動效果
這篇文章主要介紹了JavaScript實現(xiàn)余額數(shù)字滾動效果,將傳入的帶滾動的n位數(shù)字拆分成每一個要滾動的數(shù),然后動態(tài)的創(chuàng)建裝著滾動到每一位相應(yīng)數(shù)字的容器,然后放入傳入的目標(biāo)容器中,更多詳細(xì)內(nèi)容,一起進(jìn)入下面文章學(xué)習(xí)吧2021-12-12微信小程序獲取循環(huán)元素id以及wx.login登錄操作
這篇文章主要介紹了微信小程序獲取循環(huán)元素id以及wx.login登錄操作的相關(guān)資料,這里提供實例幫助大家實現(xiàn)該功能,需要的朋友可以參考下2017-08-08