使用TypeScript實(shí)現(xiàn)高效的異步隊(duì)列任務(wù)管理
在javaScript項(xiàng)目開(kāi)發(fā)中,異步編程是不可或缺的一部分。從網(wǎng)絡(luò)請(qǐng)求到延時(shí)操作,異步操作使得我們能夠在等待某個(gè)任務(wù)完成時(shí)繼續(xù)執(zhí)行其他任務(wù),提高應(yīng)用的響應(yīng)性和性能。然而,隨著應(yīng)用邏輯的復(fù)雜化,管理這些異步任務(wù)的難度也隨之增加。如何有效地組織和控制這些異步任務(wù),成為了開(kāi)發(fā)高效、可維護(hù)應(yīng)用的關(guān)鍵。本文使用JavaScript實(shí)現(xiàn)一個(gè)異步隊(duì)列來(lái)優(yōu)雅地管理復(fù)雜的異步任務(wù)流。
異步編程的挑戰(zhàn)
在深入異步隊(duì)列的實(shí)現(xiàn)之前,讓我們先回顧一下在JavaScript異步編程中常見(jiàn)的幾個(gè)挑戰(zhàn):
- 回調(diào)地獄:過(guò)度使用回調(diào)函數(shù)可能導(dǎo)致代碼難以閱讀和維護(hù),尤其是當(dāng)你有多個(gè)需要順序執(zhí)行的異步操作時(shí)。
- 并發(fā)控制:同時(shí)執(zhí)行多個(gè)異步操作時(shí),如何有效地管理它們的完成狀態(tài)并處理它們的結(jié)果。
- 錯(cuò)誤處理:在異步操作鏈中適當(dāng)?shù)夭东@和處理錯(cuò)誤。
為了解決這些問(wèn)題,許多開(kāi)發(fā)者轉(zhuǎn)向了Promise和async/await語(yǔ)法。雖然這些特性極大地改善了異步編程的體驗(yàn),但在某些場(chǎng)景下,我們?nèi)匀恍枰?xì)粒度的控制,尤其是當(dāng)我們需要按順序執(zhí)行一系列復(fù)雜的異步任務(wù),或者需要在任務(wù)間傳遞數(shù)據(jù)時(shí)。這正是異步隊(duì)列派上用場(chǎng)的時(shí)候。
異步隊(duì)列(AsyncQueue)的概念
異步隊(duì)列是一種數(shù)據(jù)結(jié)構(gòu),它按照特定的順序執(zhí)行異步任務(wù),每個(gè)任務(wù)在前一個(gè)任務(wù)完成后開(kāi)始。這種模式對(duì)于需要嚴(yán)格順序執(zhí)行的異步操作非常有用,如連續(xù)的網(wǎng)絡(luò)請(qǐng)求,其中后一個(gè)請(qǐng)求依賴(lài)于前一個(gè)請(qǐng)求的結(jié)果。
AsyncQueue類(lèi)設(shè)計(jì)
在AsyncQueue的設(shè)計(jì)中,我們將關(guān)注以下幾個(gè)關(guān)鍵部分:
- 任務(wù)的存儲(chǔ)和管理:隊(duì)列需要按順序存儲(chǔ)即將執(zhí)行的異步任務(wù)。
- 任務(wù)執(zhí)行的控制:提供方法來(lái)開(kāi)始執(zhí)行隊(duì)列中的任務(wù),并在當(dāng)前任務(wù)完成后自動(dòng)執(zhí)行下一個(gè)任務(wù)。
- 動(dòng)態(tài)任務(wù)管理:允許在隊(duì)列執(zhí)行過(guò)程中動(dòng)態(tài)添加或移除任務(wù)。
實(shí)現(xiàn)AsyncQueue
接下來(lái),我們按照先前的概述來(lái)具體實(shí)現(xiàn)AsyncQueue類(lèi)。這里用TypeScript實(shí)現(xiàn)。
步驟 1: 定義基礎(chǔ)結(jié)構(gòu)
首先,我們定義了任務(wù)(AsyncTask)的結(jié)構(gòu),以及異步任務(wù)回調(diào)(AsyncCallback)的類(lèi)型。
export type NextFunction = (nextArgs?: any) => void;
export type AsyncCallback = (
next: NextFunction,
params: any,
args: any
) => void;
interface AsyncTask {
/**
* 任務(wù)uuid
*/
uuid: number;
/**
* 任務(wù)開(kāi)始執(zhí)行的回調(diào)
* params: push時(shí)傳入的參數(shù)
* args: 上個(gè)任務(wù)傳來(lái)的參數(shù)
*/
callbacks: Array<AsyncCallback>;
/**
* 任務(wù)參數(shù)
*/
params: any;
}
NextFunction是一個(gè)函數(shù),當(dāng)當(dāng)前任務(wù)完成時(shí),它會(huì)被調(diào)用以觸發(fā)隊(duì)列中的下一個(gè)任務(wù)。AsyncCallback是任務(wù)的實(shí)際執(zhí)行函數(shù),它接收next函數(shù)、任務(wù)參數(shù)params以及上一個(gè)任務(wù)的結(jié)果args。AsyncTask接口定義了任務(wù)的結(jié)構(gòu),包括唯一標(biāo)識(shí)符uuid、回調(diào)函數(shù)數(shù)組callbacks和任務(wù)參數(shù)params。
步驟 2: 實(shí)現(xiàn)AsyncQueue類(lèi)
現(xiàn)在,開(kāi)始實(shí)現(xiàn)AsyncQueue類(lèi),它包含了任務(wù)隊(duì)列的核心邏輯。
export class AsyncQueue {
private _runningAsyncTask: AsyncTask | null = null;
private static _$uuid_count: number = 1;
private _queues: Array<AsyncTask> = [];
public get queues (): Array<AsyncTask> {
return this._queues;
}
private _isProcessingTaskUUID: number = 0;
private _enable: boolean = true;
/**
* 任務(wù)隊(duì)列完成回調(diào)
*/
public complete: Function | null = null;
constructor() {}
// 方法的具體實(shí)現(xiàn)將在后面提供
}
在AsyncQueue中,我們使用了以下幾個(gè)關(guān)鍵屬性:
_runningAsyncTask: 當(dāng)前正在執(zhí)行的任務(wù)。_$uuid_count: 用于生成任務(wù)的唯一標(biāo)識(shí)符。_queues: 存儲(chǔ)待執(zhí)行任務(wù)的隊(duì)列。_enable: 控制隊(duì)列是否可以執(zhí)行任務(wù)。complete: 任務(wù)隊(duì)列完成回調(diào)
步驟 3: 添加任務(wù)到隊(duì)列
使用push方法向隊(duì)列中添加單個(gè)任務(wù),使用pushMulti添加多個(gè)需要并發(fā)執(zhí)行的任務(wù):
/**
* push一個(gè)異步任務(wù)到隊(duì)列中
* 返回任務(wù)uuid
*/
public push (callback: AsyncCallback, params: any = null): number {
const uuid = AsyncQueue._$uuid_count++;
this._queues.push({
uuid: uuid,
callbacks: [callback],
params: params
});
return uuid;
}
/**
* push多個(gè)任務(wù),多個(gè)任務(wù)函數(shù)會(huì)同時(shí)執(zhí)行,
* 返回任務(wù)uuid
*/
public pushMulti (params: any, ...callbacks: AsyncCallback[]): number {
const uuid = AsyncQueue._$uuid_count++;
this._queues.push({
uuid: uuid,
callbacks: callbacks,
params: params
});
return uuid;
}
push和pushMulti允許動(dòng)態(tài)地向隊(duì)列中添加任務(wù),無(wú)論是單個(gè)任務(wù)還是多個(gè)任務(wù)同時(shí)執(zhí)行。
步驟 4: 移除任務(wù)和清空隊(duì)列
/** 移除一個(gè)還未執(zhí)行的異步任務(wù) */
public remove (uuid: number) {
if (this._runningAsyncTask?.uuid === uuid) {
console.warn("A running task cannot be removed");
return;
}
for (let i = 0; i < this._queues.length; i++) {
if (this._queues[i].uuid === uuid) {
this._queues.splice(i, 1);
break;
}
}
}
/**
* 清空隊(duì)列
*/
public clear () {
this._queues = [];
this._isProcessingTaskUUID = 0;
this._runningAsyncTask = null;
}
/**
* 是否有正在處理的任務(wù)
*/
public get isProcessing (): boolean {
return this._isProcessingTaskUUID > 0;
}
remove方法允許從隊(duì)列中移除尚未執(zhí)行的任務(wù),clear方法用于清空整個(gè)隊(duì)列:
步驟 5: 控制隊(duì)列執(zhí)行
play方法用于從隊(duì)列中取出任務(wù)并執(zhí)行。對(duì)于單個(gè)任務(wù),我們直接調(diào)用其回調(diào)函數(shù);對(duì)于并發(fā)任務(wù),我們同時(shí)調(diào)用它們的回調(diào)函數(shù),并等待它們?nèi)客瓿珊蟛爬^續(xù), 若隊(duì)列的大小為0,則代表異步任務(wù)隊(duì)列執(zhí)行完畢,觸發(fā)任務(wù)隊(duì)列完成回調(diào)complete。
/**
* 開(kāi)始運(yùn)行隊(duì)列
*/
public play (args: any = null) {
if (this.isProcessing) {
return;
}
if (!this._enable) {
return;
}
const actionData: AsyncTask = this._queues.shift()!;
if (actionData) {
this._runningAsyncTask = actionData;
const taskUUID: number = actionData.uuid;
this._isProcessingTaskUUID = taskUUID;
const callbacks: Array<AsyncCallback> = actionData.callbacks;
if (callbacks.length === 1) {
const nextFunc: NextFunction = (nextArgs: any = null) => {
this.next(taskUUID, nextArgs);
};
callbacks[0](nextFunc, actionData.params, args);
} else {
// 多個(gè)任務(wù)函數(shù)同時(shí)執(zhí)行
let fnum: number = callbacks.length;
const nextArgsArr: any[] = [];
const nextFunc: NextFunction = (nextArgs: any = null) => {
--fnum;
nextArgsArr.push(nextArgs || null);
if (fnum === 0) {
this.next(taskUUID, nextArgsArr);
}
};
const knum = fnum;
for (let i = 0; i < knum; i++) {
callbacks[i](nextFunc, actionData.params, args);
}
}
} else {
this._isProcessingTaskUUID = 0;
this._runningAsyncTask = null;
// console.log("任務(wù)完成")
if (this.complete) {
this.complete(args);
}
}
}
在任務(wù)執(zhí)行完成后,需要一種方式來(lái)繼續(xù)執(zhí)行隊(duì)列中的下一個(gè)任務(wù)。這是通過(guò)next方法實(shí)現(xiàn)的,該方法將根據(jù)當(dāng)前任務(wù)的uuid來(lái)確定是否繼續(xù):
protected next(taskUUID: number, args: any = null) {
if (this._isProcessingTaskUUID === taskUUID) {
this._isProcessingTaskUUID = 0;
this._runningAsyncTask = null;
this.play(args);
}
}
完整的AsyncQueue使用示例
通過(guò)AsyncQueue,我們可以很好地管理復(fù)雜的異步任務(wù)流程。以下是一個(gè)使用AsyncQueue的示例:
const queue = new AsyncQueue();
queue.push((next, params) => {
console.log("執(zhí)行任務(wù) 1");
// 模擬異步操作
setTimeout(() => {
console.log("任務(wù) 1 完成");
next();
}, 1000);
});
queue.push((next, params) => {
console.log("執(zhí)行任務(wù) 2");
setTimeout(() => {
console.log("任務(wù) 2 完成");
next();
}, 1000);
});
queue.complete = () => console.log("所有任務(wù)執(zhí)行完畢");
queue.play();
這個(gè)簡(jiǎn)單的例子展示了如何使用AsyncQueue順序執(zhí)行兩個(gè)異步任務(wù),并在所有任務(wù)完成后打印一條消息:
執(zhí)行任務(wù) 1
任務(wù) 1 完成
執(zhí)行任務(wù) 2
任務(wù) 2 完成
所有任務(wù)執(zhí)行完畢
至此,實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的異步隊(duì)列任務(wù)管理系統(tǒng)AsyncQueue, 它提供了一種高效、靈活的方式來(lái)管理和控制異步任務(wù)的執(zhí)行。通過(guò)將異步任務(wù)封裝成隊(duì)列,可以確保它們按預(yù)期的順序執(zhí)行,同時(shí)保持代碼的清晰和可維護(hù)性。這種模式特別適用于處理復(fù)雜的業(yè)務(wù)邏輯,如順序執(zhí)行網(wǎng)絡(luò)請(qǐng)求或依賴(lài)于前一個(gè)任務(wù)結(jié)果的操作。
以上就是使用TypeScript實(shí)現(xiàn)高效的異步隊(duì)列任務(wù)管理的詳細(xì)內(nèi)容,更多關(guān)于TypeScript任務(wù)隊(duì)列的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript中數(shù)組遍歷的7種方法小結(jié)
作為JavaScript開(kāi)發(fā)人員,熟悉數(shù)組的遍歷和操作是非常重要的,數(shù)組遍歷是處理和操作數(shù)組元素的基本需求之一,本文將介紹JavaScript中的7種常見(jiàn)數(shù)組遍歷方法,幫助你成為數(shù)組操作的達(dá)人2023-11-11
URL的參數(shù)中有加號(hào)傳值變?yōu)榭崭竦膯?wèn)題(URL特殊字符)
今天在調(diào)試客戶(hù)端向服務(wù)器傳遞參數(shù)時(shí),參數(shù)中的“+”全部變成了空格,原因是URL中默認(rèn)的將“+”號(hào)轉(zhuǎn)義了,經(jīng)過(guò)以下步驟解決了,需要的朋友可以參考一下2016-11-11
移動(dòng)設(shè)備手勢(shì)事件庫(kù)Touch.js使用詳解
這篇文章主要介紹了移動(dòng)設(shè)備手勢(shì)事件庫(kù)Touch.js的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
CSS和JS標(biāo)簽style屬性對(duì)照表(方便js開(kāi)發(fā)的朋友)
CSS和JS標(biāo)簽style屬性對(duì)照表(方便js開(kāi)發(fā)的朋友),需要的朋友可以收藏下。2010-11-11
詳解微信小程序?qū)崿F(xiàn)跑馬燈效果(附完整代碼)
這篇文章主要介紹了微信小程序跑馬燈效果,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
js實(shí)現(xiàn)的簡(jiǎn)單圖片浮動(dòng)效果完整實(shí)例
這篇文章主要介紹了js實(shí)現(xiàn)的簡(jiǎn)單圖片浮動(dòng)效果,以完整實(shí)例形式分析了javascript實(shí)現(xiàn)圖片定時(shí)漂浮移動(dòng)功能的方法,涉及javascript定時(shí)函數(shù)與數(shù)值動(dòng)態(tài)運(yùn)算相關(guān)技巧,需要的朋友可以參考下2016-05-05
js異步之a(chǎn)sync和await實(shí)現(xiàn)同步寫(xiě)法
本文主要介紹了js異步之a(chǎn)sync和await實(shí)現(xiàn)同步寫(xiě)法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03

