JavaScript實現一個Promise隊列小工具

摘要

在百度的解釋中,隊列是一種特殊的線性表,特殊之處在於它隻允許在表的前端進行刪除操作,而在表的後端進行插入操作,和棧一樣,隊列是一種操作受限制的線性表。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。

受這個隊列結構的啟發,在前端不同的業務場景中,由於一次性發起的異步請求過多,並且這些服務位於不同組件或者不同兄弟功能之間,我們無法直接對這些方法進行同步,所以需要引入隊列的概念,對這些並發性的問題進行順序處理。

思考

為什麼要寫一個類似於隊列的功能?

我們知道,在主流的V8引擎中,JavaScript 的執行順序都是至上而下的,引入瞭 ajax 之後我們能夠對網頁進行局部刷新,在前端領域第一次有瞭異步操作的概念。自此,當我們接觸 Promise 之後也不在陌生於這種模式。當然,使用過 Promise 的都知道,如果是在某個單一的 js 文件中,我們完全可以通過編寫一個 async 函數,通過使用 await 去實現將 Promise 函數按順序執行的功能。

那這個隊列小工具難道就是無意義的嗎?作者就像一個大渣男,玩弄瞭一番感情輕飄飄的走瞭?當然不是,大傢都知道在單一文件中,我們總有辦法去實現這樣控制異步操作的功能,那麼在不同的模塊、組件中,如何去實現這樣的操作呢?按照這個思路,我們可以發現單純的去寫是沒辦法直接解決這樣的異步功能同時觸發的問題的,這也就是作者為什麼要去些一個類似於隊列的功能。

Promise 隊列功能的思路在哪裡?

我們先要搞明白我們想要的是一個怎樣的功能呢?作者畫瞭一個大概的需要實現功能的流程圖,我們不同組件的異步請求是同時發生的,我們需要在發生之前把Promise 函數放入隊列工具,再讓隊列工具去調度出棧和入棧的順序,每次出棧的時候再去調用 Promise 函數發生請求。

我們可以在 js 文件中使用一個單例的概覽,主要是記錄入棧的順序和對操作加鎖,並且啟動一個定時服務去定期檢測棧中是否還有順序,思路是很簡單的,說幹就幹。

實現

我們新建一個 useQueue.js 文件,為瞭方便後續的引用。首先我們需要定義三個高級變量:

var queue = []
var locks = false
var timer = null
  • queue:記錄入棧的 Promise 任務函數
  • locks:在處理出棧任務時我們需要對函數加鎖,防止多次出棧
  • timer:防抖記錄,跟功能 locks 主要起一個保險左右,防止出棧時重復調用函數

我們定義兩個函數 handeruseQueue,導出默認函數 useQueue

/**
 * 處理任務出棧
 */
const hander = () => {}

/**
 * 入棧等待排隊操作
 * @param {Promise} task
 */
const useQueue = (task) => {}

/**
 * 導出
 */
export default useQueue

當我們有任務進入時,啟用 useQueue 函數,將任務放入棧中,並且設置 locks = true 進行解鎖。

const useQueue = (task) => {
  queue.push(task)
  locks = true
}

這個時候,我們在單例上設置一個心跳來定期檢測加鎖狀態,如果是解鎖則觸發處理函數。

// 心跳
setInterval(() => {
  locks && handler()
}, 100)

當函數 hander 被成果觸發,我們需要設置一個延時器來處理防抖。先進行加鎖,然後在隊列中進行出棧,如果出棧為空則解鎖;如果出棧不為空則調用任務,並且等待任務結束進入下一個 hander 。

const handler = () => {
  timer && clearTimeout(timer)
  timer = setTimeout(() => {
    locks = false
    const task = queue.shift()
    if (task) {
      task().then(handler)
    } else {
      locks = true
    }
  })
}

假設我們已經寫瞭一個 vue 字典標簽組件,組件內部會調用遠程接口,並且再記錄到某一個緩存中。如果一個頁面多次使用組件,而該緩存還並未存在,那麼可能會導致同時觸發多個異步網絡請求。這個時候就可以使用隊列工具。

import useQueue from './useQueue.js'

const handleAjax = () => {
  return new Promise(resolve => {
    ...
    resolve()
  })
}

useQueue(handleAjax)

// 或者可以這樣調用
useQueue(async () => {
  await handleAjax()
})

總結

Promise 隊列工具實際上是通過延遲觸發,來處理掛載在某一隊列數據上的出棧任務,原理其實並不復雜,就是為瞭處理異步請求同時發生的情況。當然,這個函數可以有更多的優化方案,比如說可以添加一個優先級的參數值,根據不同優先級進行入棧操作,或者增加一個同類型排異等等。

到此這篇關於JavaScript實現一個Promise隊列小工具的文章就介紹到這瞭,更多相關JS Promise隊列內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: