前端開發中,若是遇到複雜的交互邏輯,數據結構的知識將幫助你理清思路,抽象邏輯,完成穩定可靠的邏輯代碼。前端
本文就講講我在開發彈窗時加入的隊列數據結構,也許有人疑問彈窗不是很簡單嗎,還須要引入隊列?其實在複雜交互中,特別是互動類的界面中,很容易就會有超過 10 個彈窗對話框,萬一同時被觸發時,邏輯就會混亂,咱們但願一個接一個的方式彈出,這裏就須要隊列了。後端
隊列(Queue) 是先進先出(FIFO, First-In-First-Out)的線性表。在具體應用中一般用鏈表或者數組來實現。隊列只容許在尾部進行插入操做(入隊 enqueue),在頭部進行刪除操做(出隊 dequeue)。隊列的操做方式和堆棧相似,惟一的區別在於隊列只容許新數據在後端進行添加。上圖清晰的描述了隊列的特性。api
一個隊列數據結構要包含如下 api數組
使用 JavaScript/TypeScript 數組能夠模擬出這些 api,代碼以下數據結構
class Queue {
private dataStore: any[]
constructor() {
this.dataStore = []
}
public enqueue(e: any): void {
this.dataStore.push(e)
}
public dequeue() {
this.dataStore.shift()
}
public front() {
return this.dataStore[0]
}
public back() {
return this.dataStore[this.dataStore.length - 1]
}
public isEmpty(): boolean {
if (this.dataStore.length === 0) {
return true
}
return false
}
public toString() {
return this.dataStore.join(',')
}
}
export default Queue
複製代碼
能夠寫些測試用例來驗證這個隊列的功能,保證咱們的隊列運行正常ide
import Queue from './Queues'
describe('Queue', () => {
const q = new Queue()
q.enqueue(1123)
q.enqueue('chuanshi')
q.enqueue('666')
test('queue', () => {
expect(q.toString()).toBe('1123,chuanshi,666')
q.dequeue()
expect(q.toString()).toBe('chuanshi,666')
expect(q.front()).toBe('chuanshi')
expect(q.back()).toBe('666')
expect(q.isEmpty()).toBe(false)
q.dequeue()
q.dequeue()
expect(q.isEmpty()).toBe(true)
})
})
複製代碼
這樣咱們就獲得了一個隊列的數據結構。oop
彈窗被觸發喚起會有如下3種狀況:測試
爲了知足以上3種狀況,須要在主邏輯和彈窗展現之間加一個隊列控制邏輯,它們的時序圖以下:ui
上述時序圖清晰的表達了彈窗觸發的各類狀況,那麼起到關鍵做用的隊列控制(QueueCtrl)部分應該如何編寫呢?其實也很簡單,它的邏輯以下:this
當空隊列的第一個元素入隊後,上圖的右側循環部分開始啓動,同時依然能夠有元素入隊,直到右側循環邏輯將隊列全部元素出隊後,整個活動中止。
核心代碼以下:
import Queue from './Queues'
const queue = new Queue() // 實例化上文寫好的隊列類
/** * 將彈窗事件名推入隊列 */
const push = (eventName: globalEventName) => {
if (queue.isEmpty()) {
queue.enqueue(eventName)
openDialog() // 啓動出隊邏輯
} else {
queue.enqueue(eventName) // 循環中依然能夠同時入隊新的元素
}
}
/** * 打開彈窗,遞歸,循環出隊 */
const openDialog = () => {
// 打開彈窗
document.dispatchEvent(new Event(queue.front()))
// 監聽彈窗關閉
document.addEventListener(`${queue.front()}Close`, () => {
queue.dequeue() // 出隊
if (!queue.isEmpty()) { // 隊列不爲空時,遞歸
openDialog()
}
})
}
export default {
push,
}
複製代碼
只須要調用 push()
就能夠達到咱們的目的,能夠看到使用隊列這種數據結構,不到20行代碼,很是簡潔優雅的解決了這個問題!
例如這樣調用時
DialogQueue.push(globalEventName.dialogReceiveCoinsDaily)
setTimeout(() => {
DialogQueue.push(globalEventName.dialogWinLottery)
}, 1000)
setTimeout(() => {
DialogQueue.push(globalEventName.dialogReceiveCoinsDaily)
}, 1500)
setTimeout(() => {
DialogQueue.push(globalEventName.dialogWinLottery)
}, 2000)
複製代碼
會如下面的方式按順序彈出
與 Dialog 相似,爲了不屢次觸發致使的 Toast 堆疊,把每個要彈出的 Toast 內容入隊,每次彈出隊首的 Toast,每一個 Toast 完成時,出隊,並遞歸調用展現,直到隊列內容爲空。這裏不展開說明了。
例如這樣調用時
Toast.show('hi1')
Toast.show('hi2')
Toast.show('hi3')
setTimeout(() => {
Toast.show('hi5')
}, 3000);
setTimeout(() => {
Toast.show('hi4')
}, 2000);
複製代碼
會如下面的方式按順序彈出
固然上面的需求不使用隊列也能夠實現,可是隊列數據結構的意義在於可讓整個實現更加規範化、抽象化且易於維護。
熟練掌握數據結構的知識,可讓開發的過程當中思路更加清晰,代碼抽象化程度更高,更加合理的組織代碼,提升開發效率。當遇到棘手的問題時,能夠多思考一些數據結構中的知識點,說不定能夠達到事半功倍的效果呢!