Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
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 AsyncTask from 'async-task-schedule'
const asyncTask = new AsyncTask({
batchDoTasks: async (names: string[]) => {
count += 1
return names.map((n) => ([n, `${n}${count}`] as [string, string]))
},
})
asyncTask.dispatch(['a', 'b']).then(console.log)
asyncTask.dispatch(['b', 'c']).then(console.log)
asyncTask.dispatch(['d', 'c']).then(console.log)
asyncTask.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 asyncTask.dispatch(['a', 'b'])
const result2 = await asyncTask.dispatch(['b', 'c'])
const result3 = await asyncTask.dispatch(['d', 'c'])
const result4 = await asyncTask.dispatch('c')
interface IAsyncTask {
/**
* action to do batch tasks
* Task: single task request info
* Result: single task success response
*
* batchDoTasks should receive multitasks, and return tuple of task and response or error in array
* one of batchDoTasks/doTask must be specified, batchDoTasks will take priority
*/
private batchDoTasks: (tasks: Task[]) => Promise<Array<[Task, Result | Error ]>>
/**
* action to do single task
* one of batchDoTasks/doTask must be specified, batchDoTasks will take priority
*/
doTask?: ((task: Task) => Promise<Result>)
/**
* check whether two tasks are equal
* it helps to avoid duplicated tasks
* default: (a, b) => a === b
*/
private isSameTask?: (a: Task, b: Task) => boolean
/**
* max task count for batchDoTasks, default unlimited
* undefined or 0 for unlimited
*/
private 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
*/
private 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`
*/
private taskWaitingStrategy: 'throttle' | 'debounce'
/**
* task waiting time in milliseconds, default 50ms
* differently according to taskWaitingStrategy
*/
private maxWaitingGap: number
/**
* validity of the result(in ms), default unlimited
* undefined or 0 for unlimited
* cache is lazy cleaned after invalid
*/
private invalidAfter?: number
/**
* retry failed tasks next time after failing, default true
*/
private retryWhenFailed?: boolean
}
dispatch multitasks at a time, will get tuple of task and response in array
this method won't throw any error, it will fulfil even partially failed, you can check whether its success by tuple[1] instanceof Error
dispatch a task, will get response if success, or throw an 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
what you need to do is to wrap your existing task executing function into a new batchDoTasks
suppose we have a method getUsers
defined as follows:
getUsers(userIds: string[]) -> Promise<[{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 users array into tuple of user id and user info in array
return users.map(user => ([user.id, user]))
}
const getUserAsyncTask = new AsyncTask({
batchDoTasks: batchGetUsers
})
const result = await Promise.all([
getUserAsyncTask.dispatch(['user1', 'user2']),
getUserAsyncTask.dispatch(['user3', 'user2'])
])
// only one request will be sent via getUsers with userIds ['user1', 'user2', 'user3']
// request combine won't works when using await separately
const result1 = await getUserAsyncTask.dispatch(['user1', 'user2'])
const result2 = await getUserAsyncTask.dispatch(['user3', 'user2'])
if the request parameter is an object, then we should provide a isSameTask
to identify unique task
suppose we have a method decodeGeoPoints
defined as follows:
decodeGeoPoints(points: Array<{lat: number, long: number}>) -> Promise<[{point: {lat: number, long: number}, title: string, description: string, country: string}]>
then we can integrate as follows:
async function batchDecodeGeoPoints(points: Array<{lat: number, long: number}>): Promise<Array<[{lat: number, long: number}, {point: {lat: number, long: number}, title: string, description: string, country: string}]>> {
// there is no need to try/catch, errors will be handled properly
const pointInfos = await decodeGeoPoints(userIds)
// convert point info array into tuple of point and point info in array
return pointInfos.map(info => ([info.point, info]))
}
const isSamePoint = (a: {lat: number, long: number}, b: {lat: number, long: number}) => a.lat === b.lat && a.long === b.long
const decodePointsAsyncTask = new AsyncTask({
batchDoTasks: batchDecodeGeoPoints,
isSameTask: isSamePoint
})
decodePointsAsyncTask.dispatch([{lat: 23.232, long: 43.121}, {lat: 33.232, long: 11.1023}]).then(console.log)
decodePointsAsyncTask.dispatch([{lat: 23.232, long: 43.121}, {lat: 33.232, long: 44.2478}]).then(console.log)
// only one request will be sent via decodeGeoPoints
// request combine won't work when using await
const result1 = await decodePointsAsyncTask.dispatch([{lat: 23.232, long: 43.121}, {lat: 33.232, long: 11.1023}])
const result2 = await decodePointsAsyncTask.dispatch([{lat: 23.232, long: 43.121}, {lat: 33.232, long: 44.2478}]).then(console.log)
by setting taskExecStrategy
to serial
and using smaller maxBatchCount
(you can even set it to 1
), you can achieve this easily
const asyncTask = new AsyncTask({
...,
taskExecStrategy: 'serial',
maxBatchCount: 2,
})
FAQs
schedule async tasks
The npm package async-task-schedule receives a total of 14 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
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.