Security News
Supply Chain Attack Detected in Solana's web3.js Library
A supply chain attack has been detected in versions 1.95.6 and 1.95.7 of the popular @solana/web3.js library.
async-task-schedule
Advanced tools
schedule async tasks in order
yarn add async-task-schedule
# or with npm
npm install async-task-schedule -S
import TaskSchedule from 'async-task-schedule'
let count = 0
const taskSchedule = new TaskSchedule({
doTask: async (name: string) => {
count += 1
return `${name}${count}`
},
// or use this will do the same
// batchDoTasks: async (names: string[]) => {
// count += 1
// return names.map((n) => `${n}${count}`)
// },
})
taskSchedule.dispatch(['a', 'b']).then(console.log)
taskSchedule.dispatch(['b', 'c']).then(console.log)
taskSchedule.dispatch(['d', 'c']).then(console.log)
taskSchedule.dispatch('c').then(console.log)
// batchDoTasks will be only called once
NOTICE: in following example, tasks won't combine
// batchDoTasks will be executed 3 times due to javascript language features
const result1 = await taskSchedule.dispatch(['a', 'b'])
const result2 = await taskSchedule.dispatch(['b', 'c'])
const result3 = await taskSchedule.dispatch(['d', 'c'])
const result4 = await taskSchedule.dispatch('c')
options define:
// `Task` for single task's parameters
// `Result` for single task's response
interface ITaskScheduleOptions<Task, Result> {
/**
* action to do batch tasks, can be async or sync function
* Task: single task request info
* Result: single task success response
*
* batchDoTasks should receive multitasks, and return result or error in order
* one of batchDoTasks/doTask must be specified, batchDoTasks will take priority
*/
batchDoTasks: (tasks: Task[]) => Promise<Array<Result | Error>> | Array<Result | Error>
/**
* action to do single task, can be async or sync function
* one of batchDoTasks/doTask must be specified, batchDoTasks will take priority
*/
doTask?: (task: Task) => Promise<Result> | Result
/**
* check whether two tasks are equal
* it helps to avoid duplicated tasks
* default: AsyncTask.isEqual (deep equal)
*/
isSameTask?: (a: Task, b: Task) => boolean
/**
* max task count for batchDoTasks, default unlimited
* undefined or 0 for unlimited
*/
maxBatchCount?: number
/**
* batch tasks executing strategy, default parallel
* only works if maxBatchCount is specified and tasks more than maxBatchCount are executed
*
* parallel: split all tasks into a list stride by maxBatchCount, exec them at the same time
* serial: split all tasks into a list stride by maxBatchCount, exec theme one group by one group
* if serial specified, when tasks are executing, new comings will wait for them to complete
* it's especially useful to cool down task requests
*/
taskExecStrategy: 'parallel' | 'serial'
/**
* task waiting stragy, default to debounce
* throttle: tasks will combined and dispatch every `maxWaitingGap`
* debounce: tasks will combined and dispatch util no more tasks in next `maxWaitingGap`
*/
taskWaitingStrategy: 'throttle' | 'debounce'
/**
* task waiting time in milliseconds, default 50ms
* differently according to taskWaitingStrategy
*/
maxWaitingGap: number
/**
* task result caching duration(in milliseconds), default to 1000ms (1s)
* > `undefined` or `0` for unlimited
* > set to minimum value `1` to disable caching
* > `function` to specified specified each task's validity
*
* *cache is lazy cleaned after invalid*
*/
invalidAfter?: number | ((task: Task, result: Result | Error) => number)
/**
* retry failed tasks next time after failing, default true
*/
retryWhenFailed?: boolean
}
example:
import TaskSchedule from 'async-task-schedule'
const taskSchedule = new TaskSchedule({
doTask(n) {
console.log(`do task with ${n}`)
return n * n
},
invalidAfter: 0
})
const result = await taskSchedule.dispatch([1,2,3,1,2])
// get first result
const resultOf1 = result[0] // 1
// doTask won't be called
const result11 = await taskSchedule.dispatch(1) // 1
// clean all cached result
taskSchedule.cleanCache()
// doTask will be call again
const result12 = await taskSchedule.dispatch(1) // 1
dispatch multitasks at a time, will get response with corresponding order of tasks
this method won't throw any error, it will fulfil even partially failed, you can check whether its success by response instanceof Error
import TaskSchedule from 'async-task-schedule'
const taskSchedule = new TaskSchedule({
doTask(n) {
console.log(`do task with ${n}`)
if (n % 2) throw new Error(`${n} is unsupported`)
return n * n
},
invalidAfter: 0
})
const result = await taskSchedule.dispatch([1,2,3,1,2])
// get first result
const resultOf1 = result[0] // 1
// second result is error
const isError = result[1] instanceof Error // error object
try {
// will throw an error
const result2 = await taskSchedule.dispatch(2) // 1
} catch(error) {
console.warn(error)
}
dispatch a task, will get response if success, or throw an error
import TaskSchedule from 'async-task-schedule'
const taskSchedule = new TaskSchedule({
doTask(n) {
console.log(`do task with ${n}`)
if (n % 2) throw new Error(`${n} is unsupported`)
return n * n
},
invalidAfter: 0
})
const result1 = await taskSchedule.dispatch(1) // 1
try {
const result2 = await taskSchedule.dispatch(2),
} catch(error) {
console.warn(error)
}
clean cached tasks' result, so older task will trigger new request and get fresh response.
attention: this action may not exec immediately, it will take effect after all tasks are done
import TaskSchedule from 'async-task-schedule'
const taskSchedule = new TaskSchedule({
doTask(n) {
console.log(`do task with ${n}`)
return n * n
},
invalidAfter: 0
})
await Promise.all([
taskSchedule.dispatch([1, 2, 3, 1, 2]),
taskSchedule.dispatch([1, 9, 10, 12, 22]),
])
// clean all cached result
taskSchedule.cleanCache()
// task will execute again
const result = await taskSchedule.dispatch(1),
there are some utils method as static members of async-task-schedule
check whether the given values are equal (with deep comparison)
import TaskSchedule from 'async-task-schedule'
TaskSchedule.isEqual(1, '1') // false
TaskSchedule.isEqual('1', '1') // true
TaskSchedule.isEqual(NaN, NaN) // true
TaskSchedule.isEqual({a: 'a', b: 'b'}, {b: 'b', a: 'a'}) // true
TaskSchedule.isEqual({a: 'a', b: 'b', c: {e: [1,2,3]}}, {b: 'b', c: {e: [1,2,3]}, a: 'a'}) // true
TaskSchedule.isEqual({a: 'a', b: /acx/}, {b: new RegExp('acx'), a: 'a'}) // true
you can use it to check whether two tasks are equal / find specified task
what you need to do is to wrap your existing task executing function into a new batchDoTasks
fetch
suppose we use browser native fetch
to send request, we can do so to make an improvement:
const fetchSchedule = new TaskSchedule({
async doTask(cfg: {resource: string, options?: RequestInit}) {
return await fetch(cfg.resource, cfg.options)
},
// 0 for forever
// set a minimum number 1 can disable cache after 1 millisecond
invalidAfter(cfg, result) {
// cache get request for 3s
if (!cfg.options || !cfg.options.method || !cfg.options.method.toLowerCase() === 'get') {
// cache sys static config forever
if (/\/sys\/static-config$/.test(cfg.resource)) return 0
return 3000
}
// disable other types request
return 1
}
})
const betterFetch = (resource: string, options?: RequestInit) => {
return fetchSchedule.dispatch({resource, options})
}
// than you can replace fetch with betterFetch
with those codes above:
getUsers
suppose we have a method getUsers
defined as follows:
// support max 5 users at a time
getUsers(userIds: string[]) => Promise<[{code: string, message: string, id?: string, name?: string, email?: string}]>
then we can implement a batch version:
async function batchGetUsers(userIds: string[]): Promise<Array<[string, {id: string, name: string, email: string}]>> {
// there is no need to try/catch, errors will be handled properly
const users = await getUsers(userIds)
// convert invalid users to error
return users.map(user => (user.code === 'failed' ? new Error(user.message) : user))
}
const getUserSchedule = new TaskSchedule({
maxBatchCount: 5,
batchDoTasks: batchGetUsers,
// cache user info forever
invalidAfter: 0,
})
const result = await Promise.all([
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 2 requests will be sent via getUsers with userIds ['user1', 'user2', 'user3', 'user4', 'user5'] and ['user6', 'user7', 'user8', 'user9', 'user10']
// request combine won't works when using await separately
const result1 = await getUserSchedule.dispatch(['user1', 'user2'])
const result2 = await getUserSchedule.dispatch(['user3', 'user2'])
If you got a batch version function, you just need to make sure it throw an error when error occurred.
by setting taskExecStrategy
to serial
and using smaller maxBatchCount
(you can even set it to 1
), you can achieve this easily
const taskSchedule = new TaskSchedule({
...,
taskExecStrategy: 'serial',
maxBatchCount: 2,
})
FAQs
schedule async tasks
The npm package async-task-schedule receives a total of 37 weekly downloads. As such, async-task-schedule popularity was classified as not popular.
We found that async-task-schedule demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
A supply chain attack has been detected in versions 1.95.6 and 1.95.7 of the popular @solana/web3.js library.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.