Dispatch the task to the task dispatcher.
If task dispatcher dose not immediately call task.run
, the task will be pushed into pending
status otherwise it will be pushed into running
status.
Manually abort the task, and push its status into abort
If task is runnning
, the cancellation token
event within the task runnable
's handler
will be fired.
Status other than abort
and complete
will be pushed into abort
status, thus onAbort
event is fired.
Aborting the task won't directly abort real
runnable
process, because that is controlled for your own business. Certainly we suggest you to do this for you own business logic, while you're not required to do that. Task library can handle it well such that the status of the task is immediatelly changed after aborting.
It will destroy the task immedially and unless you get the task.result
and task status, you cannot do anything more to this task, neither you can start
nor abort
.
Typically you do not need to do this, until you want to manually recycle the listeners or simply just make it unavailable to other invokers.
The task library defines 8 high level events:
task.onRestart: Event<void>
task.onStart: Event<void>
task.onPending: Event<void>
task.onRunning: Event<void>
task.onError: Event<any>
task.onResult: Event<T>
task.onAbort: Event<void>
task.onComplete: Event<void>
and 2 low level events:
task.onStateChange: Event<ChangeEvent<State>>
task.onStatusChange: Event<ChangeEvent<Status>>
A task has 6 status: init
, pending
, running
, error
, abort
, complete
.
They all match the state machine graph shown at the top of this documentation.
When task status changes, it will fire the corresponding status event among the 8 high level events onXXX
, and than will invoke onStatusChange
.
After task completes before destroyed, each time you call task.onComplete
you'll get listener being invoked next event loop as if the complete
event just got fired.
When task is started on abort
or error
status, it will fire onRestart
and then onStart
.
A task has a programatical state managed by developers, and initialized on creating a new task.
When you call Task.create<R, S>(runnable: Task.Runnable<R, S>, initialState: S): Task<R, S>
, you get a task with the state type S
, the result type R
, and the initial task.state
too.
In the runnable a developer implements, the argument handler
provides 3 property getters, and a setState
method.
3 properties are:
handler.token
: the cancellation token for this taskhandler.state
: the readonly state of the current task, if you set another state, than you get a new state by callinghandler.state
handler.restart
: the restart count of this task, first time to be 0, next time 1 aftertask.start
, ...etc
handler.setState
method can change the task.state
programatically as your own customization.
The runnable
will be invoked mutiple times as you invoke start mutiple times at the right time.
You can get the final task.result
after this task is finished normally with the result.
You can get the task.error
reason object if the task is in error
status.
Every task needs a task dispatcher, which conforms to the Task.Dispatcher
interface.
Task library has already provided 2 dispatcher ready for usage.
Task.Dispatcher.Default
: A dispatcher who can dispatch task with no limitTask.Dispatcher.SingleThread
: A dispatcher who can dispatch a task only when there is no task running in it
And has also provided 1 dispatcher factory method: Dispatcher.create(maxParallel: number)
Unless a task is destroyed, you can set task.dispatcher
a new value anytime you like, in order to change the task to another dispatcher so it can be 'grouped' into one dispatcher queue.
Retry utility is used in any cases which need a retry strategy and cancellation possibility, see retry test cases for more information
Kvo utility is used to observe the object key change, and produce an Event
, see kvo test cases for more information
const a = {
foo: 0,
bar: 'init',
}
// transform any object into an `Observable` instance, which is a `Disposable`
const observable = Kvo.from(a)
// produce an `Event<Kvo.ChangeEvent<number>>`
const onFooChange = observable.observe('foo')
onFooChange(evt => console.log(evt))
/** should print out { current: 1, prev: 0, ... } */
a.foo = 1
// disconnect any observed events from this observable
observable.dispose()
/** no prints any more */
a.foo = 2
class B {
private foo: 'bar'
}
const b = new B()
// able to observe private properties, produce `Event<Kvo.ChangeEvent<string>>`
const onFooChange2 = Kvo.from(b).observe('foo')
class C extends Disposable {
public foo = 'bar'
// produce an `Event<Kvo.ChangeEvent<string>>` directly
public readonly onFooChange = Kvo.observe(this, 'foo')
}
const c = new C()
// register a listener
c.onFooChange(evt => console.log(evt))
/** should print out { current: 'bar2', prev: 'bar', ... } */
c.foo = 'bar2'
c.dispose()
// no prints any longer
c.foo = 'bar3'