-
-
Notifications
You must be signed in to change notification settings - Fork 320
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
When enabling multi-file upload, you can control the concurrency by providing the `concurrencyLimit` property. The `concurrencyLimit` property creates a `ConcurrencyRequester` instance, storing all requests in the instance's queue. The concurrent upload task quantity is restricted by the `concurrencyLimit` when using the `send` method of the instance at the end of the `uploadFiles` process.
- Loading branch information
Showing
5 changed files
with
261 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import { ConcurrencyRequestTask, UploadRequestOption } from './interface'; | ||
import { prepareData, prepareXHR } from './request'; | ||
|
||
/** | ||
* Asynchronously processes an array of items with a concurrency limit. | ||
* | ||
* @template T - Type of the input items. | ||
* @template U - Type of the result of the asynchronous task. | ||
* | ||
* @param {number} concurrencyLimit - The maximum number of asynchronous tasks to execute concurrently. | ||
* @param {T[]} items - The array of items to process asynchronously. | ||
* @param {(item: T) => Promise<U>} asyncTask - The asynchronous task to be performed on each item. | ||
* | ||
* @returns {Promise<U[]>} - A promise that resolves to an array of results from the asynchronous tasks. | ||
*/ | ||
async function asyncPool<T, U>( | ||
concurrencyLimit: number, | ||
items: T[], | ||
asyncTask: (item: T) => Promise<U>, | ||
): Promise<U[]> { | ||
const tasks: Promise<U>[] = []; | ||
const pendings: Promise<U>[] = []; | ||
|
||
for (const item of items) { | ||
const task = asyncTask(item); | ||
tasks.push(task); | ||
|
||
if (concurrencyLimit <= items.length) { | ||
task.then(() => { | ||
pendings.splice(pendings.indexOf(task), 1); | ||
}); | ||
pendings.push(task); | ||
|
||
if (pendings.length >= concurrencyLimit) { | ||
await Promise.race(pendings); | ||
} | ||
} | ||
} | ||
|
||
return Promise.all(tasks); | ||
} | ||
|
||
type DataType = 'form' | 'blob' | 'string'; | ||
|
||
/** | ||
* Represents a class for handling concurrent requests with a specified concurrency limit. | ||
* | ||
* @template T - The type of data to be uploaded. | ||
*/ | ||
export default class ConcurrencyRequester<T> { | ||
/** | ||
* The concurrency limit for handling requests simultaneously. | ||
*/ | ||
private concurrencyLimit: number; | ||
|
||
/** | ||
* An array to store the tasks for concurrent requests. | ||
*/ | ||
private tasks: ConcurrencyRequestTask[] = []; | ||
|
||
/** | ||
* The type of data to be sent in the request ('form', 'blob', or 'string'). | ||
*/ | ||
private dataType: DataType; | ||
|
||
/** | ||
* Creates an instance of ConcurrencyRequester. | ||
* | ||
* @param {number} concurrencyLimit - The concurrency limit for handling requests simultaneously. | ||
* @param {DataType} [dataType='form'] - The type of data to be sent in the request ('form', 'blob', or 'string'). | ||
*/ | ||
constructor(concurrencyLimit: number, dataType: DataType = 'form') { | ||
this.concurrencyLimit = concurrencyLimit; | ||
this.dataType = dataType; | ||
} | ||
|
||
/** | ||
* Prepares data based on the specified data type. | ||
* | ||
* @param {UploadRequestOption<T>} option - The upload request option. | ||
* @returns {string | Blob | FormData} - The prepared data based on the specified data type. | ||
* @private | ||
*/ | ||
private prepareData = (option: UploadRequestOption<T>): string | Blob | FormData => { | ||
if (this.dataType === 'form') { | ||
return prepareData(option); | ||
} | ||
|
||
return option.file; | ||
}; | ||
|
||
/** | ||
* Prepares a task for a concurrent request. | ||
* | ||
* @param {UploadRequestOption<T>} option - The upload request option. | ||
* @returns {ConcurrencyRequestTask} - The prepared task for the concurrent request. | ||
* @private | ||
*/ | ||
private prepare = (option: UploadRequestOption<T>): ConcurrencyRequestTask => { | ||
const xhr = prepareXHR(option); | ||
|
||
const data = this.prepareData(option); | ||
|
||
const task: ConcurrencyRequestTask = { xhr, data }; | ||
|
||
xhr.onerror = function error(e) { | ||
task.done?.(); | ||
xhr.onerror(e); | ||
}; | ||
|
||
xhr.onload = function onload(e) { | ||
task.done?.(); | ||
xhr.onload(e); | ||
}; | ||
|
||
return task; | ||
}; | ||
|
||
/** | ||
* Appends a new upload request to the tasks array. | ||
* | ||
* @param {UploadRequestOption<T>} option - The upload request option. | ||
* @returns {{ abort: () => void }} - An object with an `abort` function to cancel the request. | ||
*/ | ||
append = (option: UploadRequestOption<T>): { abort: () => void } => { | ||
const task = this.prepare(option); | ||
|
||
this.tasks.push(task); | ||
|
||
return { | ||
abort() { | ||
task.xhr.abort(); | ||
}, | ||
}; | ||
}; | ||
|
||
/** | ||
* Sends all the appended requests concurrently. | ||
*/ | ||
send = (): void => { | ||
asyncPool( | ||
this.concurrencyLimit, | ||
this.tasks, | ||
item => | ||
new Promise<void>(resolve => { | ||
const xhr = item.xhr; | ||
|
||
item.done = resolve; | ||
|
||
xhr.send(item.data); | ||
}), | ||
); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.