Is your feature request related to a problem?
My situation is that I have lots of tasks that are dependant on the completion of other tasks. I want to use tasuku to provide some feedback to the user on current progress of these tasks. The task dependency tree is a DAG so the visual "tree" of tasks as displayed by tasuku won't exactly match the dependency tree (for example both task A and task B could be dependant on task C so I'd need to either have to show task C as a child under only one of those tasks or have it appear twice as a child of both), but it's close enough for my purpose.
The issue is that currently I don't believe it's possible to queue up tasks and only have them run when triggered. There is currently a pending state for tasks but I believe that can only occur when the task is part of a group and that group is not set to run tasks in parallel or a concurrency value of less than the number of total grouped tasks (so a least one task will be waiting for others to finish). What I would need in order to make my tasks displayed but not started until their dependences are met, is some way to delay the start of a task.
Describe the solution you'd like
You have clearly put a lot of time and thought into the API for tasuku so you can probably think of some better ways to do this but this some different ideas I had:
import task from 'tasuku';
await task.group(task => [
// Set a delay via an options object
task('Some Task', async () => {
// Do work
return 'one';
}, {
delay: ({startTask}) => setTimeout(startTask, 5 * 1000), // As an example we wait 5
// seconds before starting the task but we could instead pass the startTask callback
// to some other code which would call it when it's ready for this task to start
}),
// Or alternatively set a delay via a method on the task instance
task('Some Task', async () => {
// do work
return 'two';
})
// The delay function would also include a cancelTask callback which could be used to
// cancel the task if needed
.delay(async ({startTask, cancelTask}) => {
await new Promise(resolve => setTimeout(resolve, 5 * 1000))
if (Math.random() > 0.5) {
startTask();
} else {
cancelTask("some cancellation reason");
}
}),
// Or instead of triggering the task start via a callback you could trigger it by a promise
// resolving
task('Some Task', async () => {
// Do work
return 'one';
}, {
delay: new Promise(resolve => setTimeout(resolve, 5 * 1000)), // Create a promise that
// resolves after 5 seconds. If this promise is rejected rather than resolved, the
// task would be marked as failed
}),
// Or the displayed state could be controlled in the task body itself
task('Some Task', async ({ setState }) => {
// Show task as pending until we're ready for it to start
setState('pending')
await new Promise(resolve => setTimeout(resolve, 5 * 1000))
// Show the task as loading
setState('loading')
// Do work
return 'one';
}),
]);
// Ideally you could also delay the start of non-grouped tasks as well. Or maybe just
// instead of grouped tasks since it seems like the main purpose of grouping tasks is
// to have tasuku manage when they run so if we're doing that ourselves then maybe
// grouping them doesn't provide any benefit.
task('Some Task', async () => {
await someAsyncTask()
}, {
delay: new Promise(resolve => setTimeout(resolve, 5 * 1000)),
})
// Since non-grouped tasks are started as soon as the task function is called you couldn't
// use the delay() task method option to modify an already created task
// (e.g. `task(...).delay(({startTask}) => startTask())`) without the breaking change of
// requiring non-grouped tasks to be started by calling a `.run()` method like
// `task(...).run()`. But you could have a .delay() method directly on the `task` object
// like the .group() method. Something like:
const someTask = task.delay('Some Task', async () => {
await someAsyncTask()
})
await new Promise(resolve => setTimeout(resolve, 5 * 1000))
someTask.start()
// or
someTask.cancel("some cancellation reason")
// If both grouped and non-grouped tasks were going to support delaying their start it would
// probably be better to use a consistent API for both however and only some of these
// syntaxes work for both.
Describe alternatives you've considered
An alternative solution would be for tasuku to provide more complex dependency management itself where you could specify that a task requires some other task to finish first. But I would imagine that this would add a lot of complexity to what is currently a very clean and tight codebase (I'm amazed you've got all the main functionality of listr2 with a codebase that is orders of magnitude smaller). Plus it would probably be a less flexible solution than adding some way to manually control the task start since other users may wish to delay the start of a task for some other reason that isn't dependant on the status of other tasuku tasks.
Currently I'm still using listr2 for the tasks that have more complex dependencies but I'd love to switch fully to tasuku.
Thanks for you consideration and for such a nice library!
Is your feature request related to a problem?
My situation is that I have lots of tasks that are dependant on the completion of other tasks. I want to use tasuku to provide some feedback to the user on current progress of these tasks. The task dependency tree is a DAG so the visual "tree" of tasks as displayed by tasuku won't exactly match the dependency tree (for example both task A and task B could be dependant on task C so I'd need to either have to show task C as a child under only one of those tasks or have it appear twice as a child of both), but it's close enough for my purpose.
The issue is that currently I don't believe it's possible to queue up tasks and only have them run when triggered. There is currently a pending state for tasks but I believe that can only occur when the task is part of a group and that group is not set to run tasks in parallel or a concurrency value of less than the number of total grouped tasks (so a least one task will be waiting for others to finish). What I would need in order to make my tasks displayed but not started until their dependences are met, is some way to delay the start of a task.
Describe the solution you'd like
You have clearly put a lot of time and thought into the API for tasuku so you can probably think of some better ways to do this but this some different ideas I had:
Describe alternatives you've considered
An alternative solution would be for tasuku to provide more complex dependency management itself where you could specify that a task requires some other task to finish first. But I would imagine that this would add a lot of complexity to what is currently a very clean and tight codebase (I'm amazed you've got all the main functionality of listr2 with a codebase that is orders of magnitude smaller). Plus it would probably be a less flexible solution than adding some way to manually control the task start since other users may wish to delay the start of a task for some other reason that isn't dependant on the status of other tasuku tasks.
Currently I'm still using listr2 for the tasks that have more complex dependencies but I'd love to switch fully to tasuku.
Thanks for you consideration and for such a nice library!