連續同源異步操作隊列

NO IMAGE

問題的發現

來自同源的多個異步操作可能引起異步衝突問題,特別是在網絡請求時。同源操作產生了兩個ajax請求,它們的請求結果將用於渲染同一個區域,然而由於網絡問題,先發出的請求後返回,導致最終得到的界面是錯誤的。

解決這個問題的最好辦法,是利用原生XHR的abort方法,在後一次操作時,將前一次操作引起的ajax請求給cancel掉。

但是在現實條件下,異步操作並非都有cancel操作。js原生的Promise沒有,原生的fetch基於Promise也沒有。基於Promise的很多工具都沒有cancel操作。這種情況下怎麼解決這個問題呢?

其實方法是有的,就是直接丟棄Promise的推送,不執行它的resolve回調即可。這樣,雖然異步操作已經執行了,但不會對現有的環境造成任何副作用。(雖然這樣看上去浪費了異步操作這個資源。)

問題的思考

如何來判斷是否要丟掉它的回調?我們可以創建一個隊列,每次產生一個異步操作時,就將它加入到隊列中,當隊列中存在操作對象時,每次只取最後一個,等待它推送結果,執行它的回調,排在它前面的操作全部丟棄掉。

基於這樣的想法,我寫了一個工具。它為異步操作創建隊列,並根據不同的場景實現不同的隊列操作形式。你可以通過這裡閱讀它的源碼和文檔。它提供了4種可供選擇的場景,開發者根據自己的實際場景選擇使用其中的一種。

問題的解決

基於上述的思考,我最終發佈了deferer-queue,你可以通過npm安裝這個包,在自己的項目中使用它。它的操作模式超級簡單,首先實例化一個queue對象,然後往這個queue push異步操作,異步操作被裝在一個函數中被push進隊列,它的回調函數一定是按照push的順序執行。

import DefererQueue from 'deferer-queue'
const queue = new DefererQueue()
const defer1 = () => new Promise((resolve, reject) => { ... })
const defer2 = () => axios.get(...)
const defer3 = async () => ...
queue.push(defer1).then(() => { console.log(1) })
queue.push(defer2).then(() => { console.log(2) })
queue.push(defer3).then(() => { console.log(3) })

無論defer1-3中的誰,執行後誰先返回結果,控制檯輸出的結果永遠是1 2 3(defer都成功的前提下),因為deferer-queue設計的就是保證回調是按push順序執行。

要使用不同模式,只需要在實例化的時候,傳入一個options對象,將mode設置為parallel/serial/switch/shift中的一個即可:

const queue = new DefererQueue({
mode: 'switch',
})

其他的用法一樣。這樣,你的隊列就會只採用最後一個push進隊列的defer的結果,即使你的隊列正在進行,只要有新的defer被push進去,那之前的所有操作都會被丟掉,隊列運行結果只取最後一個操作的結果。

關於具體的API使用細則,你可以閱讀它的文檔。其中,利用axios的cancel能力那個地方非常有借鑑意義。

關於四種運行模式的詳細解釋,由於圖片限制,請到我的博客進行閱讀。

相關文章

前端驅動的接口數據檢查、文檔生成、mock以及接口自動化測試

React中的雙向綁定

學不動系列:新前端框架Nautil,哇~

TySheMo前端數據管理模型