async-task-schedule
Advanced tools
Comparing version 1.0.0 to 1.0.1
@@ -24,3 +24,2 @@ function $parcel$export(e, n, v, s) { | ||
this.pendingTasks = []; | ||
this.doingTasks = []; | ||
this.doneTaskMap = []; | ||
@@ -33,7 +32,9 @@ this.taskQueue = []; | ||
if (!userOptions.batchDoTasks && !userOptions.doTask) throw new Error("one of batchDoTasks / doTask must be specified"); | ||
this.batchDoTasks = userOptions.batchDoTasks || $d415641d0cfd8c85$export$37f92047f3e3e1d6.wrapDoTask(userOptions.doTask); | ||
this.doTask = userOptions.doTask; | ||
this.batchDoTasks = userOptions.batchDoTasks; | ||
this.taskExecStrategy = userOptions.taskExecStrategy; | ||
if (this.taskExecStrategy === "serial") this.maxBatchCount = 1; | ||
this.retryWhenFailed = userOptions.retryWhenFailed; | ||
this.invalidAfter = userOptions.invalidAfter; | ||
this.tryTodDoTasks = this.tryTodDoTasks.bind(this); | ||
this.runTasks = this.runTasks.bind(this); | ||
this.dispatch = this.dispatch.bind(this); | ||
@@ -48,3 +49,3 @@ } | ||
} catch (error) { | ||
// not found | ||
// note all tasks are cached, just created new tasks | ||
} | ||
@@ -57,3 +58,4 @@ return new Promise((resolve, reject)=>{ | ||
* clean cached task result | ||
* this may not exec immediately, it will take effect after all tasks are done | ||
* - this may not exec immediately | ||
* - it will take effect after all tasks are done | ||
*/ cleanCache() { | ||
@@ -67,3 +69,3 @@ this.needCleanCache = true; | ||
if (!this.needCleanCache) return; | ||
if (this.pendingTasks.length || this.doingTasks.length || this.taskQueue.length) return; | ||
if (this.pendingTasks.length || this.taskQueue.length) return; | ||
this.needCleanCache = false; | ||
@@ -90,4 +92,2 @@ this.doneTaskMap = []; | ||
if (this.pendingTasks.length) myTasks = myTasks.filter((f)=>!this.hasTask(this.pendingTasks, f)); | ||
// remove doing tasks | ||
if (myTasks.length && this.doingTasks.length) myTasks = myTasks.filter((f)=>!this.hasTask(this.doingTasks, f)); | ||
// remove done tasks | ||
@@ -104,44 +104,49 @@ if (myTasks.length) myTasks = myTasks.filter((f)=>!this.getTaskResult(f)); | ||
} else timeout = this.maxWaitingGap; | ||
this.timeoutId = setTimeout(this.tryTodDoTasks, timeout); | ||
this.timeoutId = setTimeout(this.runTasks, timeout); | ||
} | ||
/** | ||
* try to do tasks | ||
* if taskExecStrategy is parallel then do it immediately, | ||
* otherwise waiting util doingTasks is empty | ||
*/ tryTodDoTasks() { | ||
// should exec in serial, and still has executing tasks | ||
if (this.taskExecStrategy === "serial" && this.doingTasks.length) { | ||
clearTimeout(this.delayTimeoutId); | ||
// wait a moment then check again | ||
this.delayTimeoutId = setTimeout(this.tryTodDoTasks, 50); | ||
} else this.doTasks(); | ||
// whether task is running | ||
isTaskRunning = false; | ||
runTasks() { | ||
if (this.isTaskRunning || !this.pendingTasks.length) return; | ||
this.isTaskRunning = true; | ||
if (this.batchDoTasks) this.runTaskWithBatchDoTasks(); | ||
else this.runTasksWithDoTask(); | ||
} | ||
async doTasks() { | ||
const tasks = this.pendingTasks.splice(0); | ||
this.doingTasks = this.doingTasks.concat(tasks); | ||
const tasksGroup = this.maxBatchCount ? $d415641d0cfd8c85$export$37f92047f3e3e1d6.chunk(tasks, this.maxBatchCount) : [ | ||
tasks | ||
]; | ||
if (this.taskExecStrategy === "serial") // eslint-disable-next-line no-plusplus | ||
for(let index = 0; index < tasksGroup.length; ++index){ | ||
const taskList = tasksGroup[index]; | ||
async runTasksWithDoTask() { | ||
const taskItems = this.pendingTasks.splice(0, this.maxBatchCount || this.pendingTasks.length); | ||
taskItems.forEach(async (task)=>{ | ||
try { | ||
// eslint-disable-next-line no-await-in-loop | ||
const result = await this.batchDoTasks(taskList); | ||
this.updateResultMap(taskList, result); | ||
const result = await this.doTask(task); | ||
this.updateResultMap([ | ||
task | ||
], [ | ||
result | ||
]); | ||
} catch (error) { | ||
this.updateResultMap(taskList, $d415641d0cfd8c85$export$37f92047f3e3e1d6.wrapError(error)); | ||
this.updateResultMap([ | ||
task | ||
], $d415641d0cfd8c85$export$37f92047f3e3e1d6.wrapError(error)); | ||
} | ||
this.checkAllTasks(); | ||
this.removeDoneTasks(taskList); | ||
if (this.pendingTasks.length) this.runTasksWithDoTask(); | ||
else { | ||
this.cleanupTasks(); | ||
this.isTaskRunning = false; | ||
} | ||
}); | ||
} | ||
async runTaskWithBatchDoTasks() { | ||
const taskItems = this.pendingTasks.splice(0, this.maxBatchCount || this.pendingTasks.length); | ||
try { | ||
const result = await this.batchDoTasks(taskItems); | ||
this.updateResultMap(taskItems, result); | ||
} catch (error) { | ||
this.updateResultMap(taskItems, $d415641d0cfd8c85$export$37f92047f3e3e1d6.wrapError(error)); | ||
} | ||
this.checkAllTasks(); | ||
if (this.pendingTasks.length) this.runTaskWithBatchDoTasks(); | ||
else { | ||
const allResponse = await Promise.all(tasksGroup.map((taskList)=>$d415641d0cfd8c85$export$37f92047f3e3e1d6.runTaskExecutor(this.batchDoTasks, taskList))); | ||
allResponse.forEach((result, index)=>{ | ||
this.updateResultMap(tasksGroup[index], result.status === "rejected" ? $d415641d0cfd8c85$export$37f92047f3e3e1d6.wrapError(result.reason) : result.value); | ||
}); | ||
this.checkAllTasks(); | ||
this.removeDoneTasks(tasks); | ||
this.cleanupTasks(); | ||
this.isTaskRunning = false; | ||
} | ||
this.cleanupTasks(); | ||
} | ||
@@ -176,3 +181,3 @@ /** | ||
return tasks.reduce((acc, task)=>{ | ||
const val = this.getTaskResult(task) || false; | ||
const val = this.getTaskResult(task); | ||
if (!val) throw new Error("not found"); | ||
@@ -183,3 +188,3 @@ acc.push(val[1]); | ||
} | ||
const val = this.getTaskResult(tasks) || false; | ||
const val = this.getTaskResult(tasks); | ||
if (!val) throw new Error("not found"); | ||
@@ -198,5 +203,2 @@ return val[1]; | ||
} | ||
removeDoneTasks(tasks) { | ||
this.doingTasks = this.doingTasks.filter((f)=>!this.hasTask(tasks, f)); | ||
} | ||
updateResultMap(tasks, result) { | ||
@@ -216,3 +218,3 @@ const now = Date.now(); | ||
task: t, | ||
value: taskResult ? taskResult : defaultValue, | ||
value: taskResult, | ||
time: now | ||
@@ -226,4 +228,4 @@ }; | ||
* clean tasks | ||
* try to clean cache if needed | ||
* try to remove failed result, remove outdated cache if needed | ||
* - try to clean cache if needed | ||
* - try to remove failed result, remove outdated cache if needed | ||
*/ cleanupTasks() { | ||
@@ -233,2 +235,4 @@ this.cleanCacheIfNeeded(); | ||
if (this.taskQueue.length) return; | ||
// nothing to cleanup | ||
if (!this.doneTaskMap.length) return; | ||
// no need to remove outdated or failed tasks | ||
@@ -240,8 +244,5 @@ if (!this.invalidAfter && !this.retryWhenFailed) return; | ||
if (this.invalidAfter) { | ||
const time = typeof this.invalidAfter === "function" ? this.invalidAfter([ | ||
item.task, | ||
item.value | ||
]) : this.invalidAfter; | ||
if (!time) return true; | ||
return now - item.time <= this.invalidAfter; | ||
const invalidAfter = typeof this.invalidAfter === "function" ? this.invalidAfter(item.task, item.value) : this.invalidAfter; | ||
if (!invalidAfter) return true; | ||
return now - item.time <= invalidAfter; | ||
} | ||
@@ -262,12 +263,2 @@ return true; | ||
/** | ||
* split array to chunks with specified size | ||
* @param arr array of fileIds | ||
* @param size chunk size | ||
* @returns 2 dimensional array | ||
*/ static chunk(arr, size) { | ||
const result = []; | ||
for(let i = 0; i < arr.length; i += size)result.push(arr.slice(i, i + size)); | ||
return result; | ||
} | ||
/** | ||
* simulate Promise.allSettled result item for better compatibility | ||
@@ -292,15 +283,2 @@ * (due to Promise.allSettled only support newer platforms) | ||
/** | ||
* wrap do task to a batch version | ||
* @param doTask action to do single task | ||
* @returns batch version to do multi tasks | ||
*/ static wrapDoTask(doTask) { | ||
return async function(tasks) { | ||
const results = await Promise.all(tasks.map((t)=>$d415641d0cfd8c85$export$37f92047f3e3e1d6.runTaskExecutor(doTask, t))); | ||
return tasks.map((t, idx)=>{ | ||
const result = results[idx]; | ||
return result.status === "fulfilled" ? result.value : result.reason; | ||
}); | ||
}; | ||
} | ||
/** | ||
* check whether the given values are equal (with deep comparison) | ||
@@ -307,0 +285,0 @@ */ static isEqual(a, b) { |
@@ -23,3 +23,2 @@ function $parcel$export(e, n, v, s) { | ||
this.pendingTasks = []; | ||
this.doingTasks = []; | ||
this.doneTaskMap = []; | ||
@@ -32,7 +31,9 @@ this.taskQueue = []; | ||
if (!userOptions.batchDoTasks && !userOptions.doTask) throw new Error("one of batchDoTasks / doTask must be specified"); | ||
this.batchDoTasks = userOptions.batchDoTasks || $f5bfd4ce37214f4f$export$37f92047f3e3e1d6.wrapDoTask(userOptions.doTask); | ||
this.doTask = userOptions.doTask; | ||
this.batchDoTasks = userOptions.batchDoTasks; | ||
this.taskExecStrategy = userOptions.taskExecStrategy; | ||
if (this.taskExecStrategy === "serial") this.maxBatchCount = 1; | ||
this.retryWhenFailed = userOptions.retryWhenFailed; | ||
this.invalidAfter = userOptions.invalidAfter; | ||
this.tryTodDoTasks = this.tryTodDoTasks.bind(this); | ||
this.runTasks = this.runTasks.bind(this); | ||
this.dispatch = this.dispatch.bind(this); | ||
@@ -47,3 +48,3 @@ } | ||
} catch (error) { | ||
// not found | ||
// note all tasks are cached, just created new tasks | ||
} | ||
@@ -56,3 +57,4 @@ return new Promise((resolve, reject)=>{ | ||
* clean cached task result | ||
* this may not exec immediately, it will take effect after all tasks are done | ||
* - this may not exec immediately | ||
* - it will take effect after all tasks are done | ||
*/ cleanCache() { | ||
@@ -66,3 +68,3 @@ this.needCleanCache = true; | ||
if (!this.needCleanCache) return; | ||
if (this.pendingTasks.length || this.doingTasks.length || this.taskQueue.length) return; | ||
if (this.pendingTasks.length || this.taskQueue.length) return; | ||
this.needCleanCache = false; | ||
@@ -89,4 +91,2 @@ this.doneTaskMap = []; | ||
if (this.pendingTasks.length) myTasks = myTasks.filter((f)=>!this.hasTask(this.pendingTasks, f)); | ||
// remove doing tasks | ||
if (myTasks.length && this.doingTasks.length) myTasks = myTasks.filter((f)=>!this.hasTask(this.doingTasks, f)); | ||
// remove done tasks | ||
@@ -103,44 +103,49 @@ if (myTasks.length) myTasks = myTasks.filter((f)=>!this.getTaskResult(f)); | ||
} else timeout = this.maxWaitingGap; | ||
this.timeoutId = setTimeout(this.tryTodDoTasks, timeout); | ||
this.timeoutId = setTimeout(this.runTasks, timeout); | ||
} | ||
/** | ||
* try to do tasks | ||
* if taskExecStrategy is parallel then do it immediately, | ||
* otherwise waiting util doingTasks is empty | ||
*/ tryTodDoTasks() { | ||
// should exec in serial, and still has executing tasks | ||
if (this.taskExecStrategy === "serial" && this.doingTasks.length) { | ||
clearTimeout(this.delayTimeoutId); | ||
// wait a moment then check again | ||
this.delayTimeoutId = setTimeout(this.tryTodDoTasks, 50); | ||
} else this.doTasks(); | ||
// whether task is running | ||
isTaskRunning = false; | ||
runTasks() { | ||
if (this.isTaskRunning || !this.pendingTasks.length) return; | ||
this.isTaskRunning = true; | ||
if (this.batchDoTasks) this.runTaskWithBatchDoTasks(); | ||
else this.runTasksWithDoTask(); | ||
} | ||
async doTasks() { | ||
const tasks = this.pendingTasks.splice(0); | ||
this.doingTasks = this.doingTasks.concat(tasks); | ||
const tasksGroup = this.maxBatchCount ? $f5bfd4ce37214f4f$export$37f92047f3e3e1d6.chunk(tasks, this.maxBatchCount) : [ | ||
tasks | ||
]; | ||
if (this.taskExecStrategy === "serial") // eslint-disable-next-line no-plusplus | ||
for(let index = 0; index < tasksGroup.length; ++index){ | ||
const taskList = tasksGroup[index]; | ||
async runTasksWithDoTask() { | ||
const taskItems = this.pendingTasks.splice(0, this.maxBatchCount || this.pendingTasks.length); | ||
taskItems.forEach(async (task)=>{ | ||
try { | ||
// eslint-disable-next-line no-await-in-loop | ||
const result = await this.batchDoTasks(taskList); | ||
this.updateResultMap(taskList, result); | ||
const result = await this.doTask(task); | ||
this.updateResultMap([ | ||
task | ||
], [ | ||
result | ||
]); | ||
} catch (error) { | ||
this.updateResultMap(taskList, $f5bfd4ce37214f4f$export$37f92047f3e3e1d6.wrapError(error)); | ||
this.updateResultMap([ | ||
task | ||
], $f5bfd4ce37214f4f$export$37f92047f3e3e1d6.wrapError(error)); | ||
} | ||
this.checkAllTasks(); | ||
this.removeDoneTasks(taskList); | ||
if (this.pendingTasks.length) this.runTasksWithDoTask(); | ||
else { | ||
this.cleanupTasks(); | ||
this.isTaskRunning = false; | ||
} | ||
}); | ||
} | ||
async runTaskWithBatchDoTasks() { | ||
const taskItems = this.pendingTasks.splice(0, this.maxBatchCount || this.pendingTasks.length); | ||
try { | ||
const result = await this.batchDoTasks(taskItems); | ||
this.updateResultMap(taskItems, result); | ||
} catch (error) { | ||
this.updateResultMap(taskItems, $f5bfd4ce37214f4f$export$37f92047f3e3e1d6.wrapError(error)); | ||
} | ||
this.checkAllTasks(); | ||
if (this.pendingTasks.length) this.runTaskWithBatchDoTasks(); | ||
else { | ||
const allResponse = await Promise.all(tasksGroup.map((taskList)=>$f5bfd4ce37214f4f$export$37f92047f3e3e1d6.runTaskExecutor(this.batchDoTasks, taskList))); | ||
allResponse.forEach((result, index)=>{ | ||
this.updateResultMap(tasksGroup[index], result.status === "rejected" ? $f5bfd4ce37214f4f$export$37f92047f3e3e1d6.wrapError(result.reason) : result.value); | ||
}); | ||
this.checkAllTasks(); | ||
this.removeDoneTasks(tasks); | ||
this.cleanupTasks(); | ||
this.isTaskRunning = false; | ||
} | ||
this.cleanupTasks(); | ||
} | ||
@@ -175,3 +180,3 @@ /** | ||
return tasks.reduce((acc, task)=>{ | ||
const val = this.getTaskResult(task) || false; | ||
const val = this.getTaskResult(task); | ||
if (!val) throw new Error("not found"); | ||
@@ -182,3 +187,3 @@ acc.push(val[1]); | ||
} | ||
const val = this.getTaskResult(tasks) || false; | ||
const val = this.getTaskResult(tasks); | ||
if (!val) throw new Error("not found"); | ||
@@ -197,5 +202,2 @@ return val[1]; | ||
} | ||
removeDoneTasks(tasks) { | ||
this.doingTasks = this.doingTasks.filter((f)=>!this.hasTask(tasks, f)); | ||
} | ||
updateResultMap(tasks, result) { | ||
@@ -215,3 +217,3 @@ const now = Date.now(); | ||
task: t, | ||
value: taskResult ? taskResult : defaultValue, | ||
value: taskResult, | ||
time: now | ||
@@ -225,4 +227,4 @@ }; | ||
* clean tasks | ||
* try to clean cache if needed | ||
* try to remove failed result, remove outdated cache if needed | ||
* - try to clean cache if needed | ||
* - try to remove failed result, remove outdated cache if needed | ||
*/ cleanupTasks() { | ||
@@ -232,2 +234,4 @@ this.cleanCacheIfNeeded(); | ||
if (this.taskQueue.length) return; | ||
// nothing to cleanup | ||
if (!this.doneTaskMap.length) return; | ||
// no need to remove outdated or failed tasks | ||
@@ -239,8 +243,5 @@ if (!this.invalidAfter && !this.retryWhenFailed) return; | ||
if (this.invalidAfter) { | ||
const time = typeof this.invalidAfter === "function" ? this.invalidAfter([ | ||
item.task, | ||
item.value | ||
]) : this.invalidAfter; | ||
if (!time) return true; | ||
return now - item.time <= this.invalidAfter; | ||
const invalidAfter = typeof this.invalidAfter === "function" ? this.invalidAfter(item.task, item.value) : this.invalidAfter; | ||
if (!invalidAfter) return true; | ||
return now - item.time <= invalidAfter; | ||
} | ||
@@ -261,12 +262,2 @@ return true; | ||
/** | ||
* split array to chunks with specified size | ||
* @param arr array of fileIds | ||
* @param size chunk size | ||
* @returns 2 dimensional array | ||
*/ static chunk(arr, size) { | ||
const result = []; | ||
for(let i = 0; i < arr.length; i += size)result.push(arr.slice(i, i + size)); | ||
return result; | ||
} | ||
/** | ||
* simulate Promise.allSettled result item for better compatibility | ||
@@ -291,15 +282,2 @@ * (due to Promise.allSettled only support newer platforms) | ||
/** | ||
* wrap do task to a batch version | ||
* @param doTask action to do single task | ||
* @returns batch version to do multi tasks | ||
*/ static wrapDoTask(doTask) { | ||
return async function(tasks) { | ||
const results = await Promise.all(tasks.map((t)=>$f5bfd4ce37214f4f$export$37f92047f3e3e1d6.runTaskExecutor(doTask, t))); | ||
return tasks.map((t, idx)=>{ | ||
const result = results[idx]; | ||
return result.status === "fulfilled" ? result.value : result.reason; | ||
}); | ||
}; | ||
} | ||
/** | ||
* check whether the given values are equal (with deep comparison) | ||
@@ -306,0 +284,0 @@ */ static isEqual(a, b) { |
@@ -35,3 +35,3 @@ export type ITaskExecStrategy = 'parallel' | 'serial'; | ||
*/ | ||
invalidAfter?: number | ((cached: readonly [Task, Result | Error]) => number); | ||
invalidAfter?: number | ((task: Task, result: Result | Error) => number); | ||
/** | ||
@@ -42,3 +42,3 @@ * retry failed tasks next time after failing, default true | ||
/** | ||
* task waiting stragy, default to debounce | ||
* task waiting strategy, default to debounce | ||
* throttle: tasks will combined and dispatch every `maxWaitingGap` | ||
@@ -65,5 +65,7 @@ * debounce: tasks will combined and dispatch util no more tasks in next `maxWaitingGap` | ||
* clean cached task result | ||
* this may not exec immediately, it will take effect after all tasks are done | ||
* - this may not exec immediately | ||
* - it will take effect after all tasks are done | ||
*/ | ||
cleanCache(): void; | ||
isTaskRunning: boolean; | ||
/** | ||
@@ -75,9 +77,2 @@ * wrap error info, if it's not instanceof Error, wrap it with Error | ||
/** | ||
* split array to chunks with specified size | ||
* @param arr array of fileIds | ||
* @param size chunk size | ||
* @returns 2 dimensional array | ||
*/ | ||
static chunk<T>(arr: T[], size: number): T[][]; | ||
/** | ||
* simulate Promise.allSettled result item for better compatibility | ||
@@ -96,8 +91,2 @@ * (due to Promise.allSettled only support newer platforms) | ||
/** | ||
* wrap do task to a batch version | ||
* @param doTask action to do single task | ||
* @returns batch version to do multi tasks | ||
*/ | ||
static wrapDoTask<T, R>(doTask: (t: T) => Promise<R> | R): (tasks: T[]) => Promise<Array<R | Error>>; | ||
/** | ||
* check whether the given values are equal (with deep comparison) | ||
@@ -104,0 +93,0 @@ */ |
{ | ||
"name": "async-task-schedule", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"description": "schedule async tasks", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -142,3 +142,3 @@ # async-task-schedule | ||
*/ | ||
invalidAfter?: number | ((cached: readonly [Task, Result | Error]) => number) | ||
invalidAfter?: number | ((task: Task, result: Result | Error) => number) | ||
@@ -262,12 +262,3 @@ /** | ||
### chunk<T>(arr: T[], size: number): T[][] | ||
split array to chunks with specified size | ||
```ts | ||
import TaskSchedule from 'async-task-schedule' | ||
const chunked = TaskSchedule.chunk([1,2,3,4,5,6,7], 3) | ||
// [[1,2,3], [4,5,6], [7]] | ||
``` | ||
### isEqual(a: unknown, b: unknown): boolean | ||
@@ -305,3 +296,3 @@ check whether the given values are equal (with deep comparison) | ||
// set a minimum number 1 can disable cache after 1 millisecond | ||
invalidAfter([cfg, result]) { | ||
invalidAfter(cfg, result) { | ||
// cache get request for 3s | ||
@@ -334,2 +325,3 @@ if (!cfg.options || !cfg.options.method || !cfg.options.method.toLowerCase() === 'get') { | ||
```ts | ||
// support max 5 users at a time | ||
getUsers(userIds: string[]) => Promise<[{code: string, message: string, id?: string, name?: string, email?: string}]> | ||
@@ -348,2 +340,3 @@ ``` | ||
const getUserSchedule = new TaskSchedule({ | ||
maxBatchCount: 5, | ||
batchDoTasks: batchGetUsers, | ||
@@ -355,6 +348,8 @@ // cache user info forever | ||
const result = await Promise.all([ | ||
getUserSchedule.dispatch(['user1', 'user2']), | ||
getUserSchedule.dispatch(['user1', 'user2', 'user3', 'user4', 'user5', 'user6']), | ||
getUserSchedule.dispatch(['user3', 'user2']) | ||
getUserSchedule.dispatch(['user6', 'user7', 'user8', 'user9', 'user10']) | ||
getUserSchedule.dispatch(['user2', 'user6', 'user9']) | ||
]) | ||
// only one request will be sent via getUsers with userIds ['user1', 'user2', 'user3'] | ||
// only 2 requests will be sent via getUsers with userIds ['user1', 'user2', 'user3', 'user4', 'user5'] and ['user6', 'user7', 'user8', 'user9', 'user10'] | ||
@@ -361,0 +356,0 @@ // request combine won't works when using await separately |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
37852
675
370