To execute and manage asynchronous functions in sequence.
- Start
- Usage
- Something About Steps.
- API
- Sequence( steps, options = {} )
- Sequence.prototype.append( steps )
- Sequence.prototype.clear()
- Sequence.prototype.next()
- Sequence.prototype.go( index )
- Sequence.prototype.run()
- Sequence.prototype.stop()
- Sequence.prototype.suspend( duration = 1000 )
- Sequence.all( steps[, interval = 0, callback ] )
- Sequence.chain( steps[, interval = 0, callback ] )
- Sequence.any( steps[, interval = 0, callback ] )
- Sequence.Error
To install the package with npm
.
$ npm i @lvchengbin/sequence --save
We provide another version which is using the native Promise
and other ES6
features, and you can intall it with the @next
tag:
$ npm i @lvchengbin/sequence@next --save
Then you can use it in nodejs code.
const Sequence = require( '@lvchengbin/sequence' );
If you want to use the package as an ES6 module, you can import it like this:
import Sequence from '@lvchengbin/sequence';
We also provide files for using in browsers with <script>
tag, you can get it here sequence.js, and if you want to use it in browsers not supporting ES6 syntax, please use sequence.bc.js.
To create a simple sequence with new Sequence
:
import Sequence from '@lvchengbin/sequence';
const steps = [
() => Promise.resolve(),
() => Promise.reject(),
() => true
];
const sequence = new Sequence( steps, { interval : 1000 } );
sequence.on( 'success', ( data, index ) => {
// execute when each step in sequence succeed
} );
sequence.on( 'failed', ( data, index ) => {
this.go( index - 1 );
// execute when each step in sequence failed
} );
sequence.on( 'end', () => {
// execute after finishing all steps in the sequence
} );
In the code above, the functions in steps
will execute one by one, no metter if one of them is succeed or failed, and you can get information and status by listening events success
, failed
and end
.
Or you can create a sequence without auto running, and to execute each step by calling the next
method:
const sequence = new Sequence( steps, { autorun : false } );
const result = await sequence.next();
if( result.status === Sequence.SUCCEEDED ) {
sequence.next();
}
And you can easily create a sequence, which will execute like Promise.all
, with Sequence.all
:
Sequence.all( steps ).then( results => {
// some code...
} ).catch( e => {
console.error( e );
} );
A method Sequence.chain
is provided for running a sequence by ignoring the result (succeeded or failed) of each step:
Sequence.chain( steps ).then( results => {
// some code...
} );
For example, if you want to send a request after user click a button, but you need to make all requests be sent one by one if the user clicked the button multiple times:
const sequence = new Sequence();
sequence.on( 'failed', function() {
alert( 'Something error happened' );
this.stop();
} );
const button = document.querySelector( '#button' );
button.addEventListener( 'click', () => {
sequence.append( () => {
return request( 'the url' );
} );
} );
Each step in sequence should be a function, and generally, it should return a Promise object. If the return value of one step function is not a Promise object, the return value will be converted to a Promise object with Promise.resolve( {the return value} )
. Each step function in the sequence will be executed after its previous step function finished (no matter the Promise object is resolved or rejected).
There are three arguments will be passed to the step function, like this:
const step = ( result, index, results ) {
if( result.status === Sequence.SUCCEEDED ) {
console.log( result.value );
// some code...
} else {
console.log( result.reason );
}
}
-
result : the result of the previous step, the structure of the result is:
// result of a successful step { status : Sequence.SUCCEEDED, index : 1, value : 'some value here', time : 1516339895559 } // result of a failed step { status : Sequence.FAILED, index : 2, reason : 'some reason here', time : 1516339895559 }
- status : the status of the result, it can be
Sequence.SUCCEEDED
orSequence.FAILED
. - index : the position of the step in the sequence.
- value : the value of the resolved promise of the step.
- reason : the failed reason of the rejecte promise of the step.
- time : the time that the step finished.
- status : the status of the result, it can be
-
index : the order of the step in sequence
-
results : the full result list of all finished steps.
Parameters
-
steps
Function
|Iterable
The steps that you want to add to the sequence, each step must be a
Function
and should return a Promise object, if the return value of a step is not a Promise object, it will be converted to a resolved Promise instance as its value is the return value. -
options
Object
Options for the sequence, this is the list of options which are supported:
-
autorun
Boolean
- Default : trueDenoting if the sequence should be run automatically after creating.
-
interval
Number
- Default : 0The interval between each step, the unit is
ms
.
-
Properties of Sequence instance
- running : denoting if the sequence is running.
- suspended : denoting if the sequence is suspended.
- busy : denoting if there is a step in the sequence is in process.
- interval : the interval between executing two steps, you can change is as you want.
- steps : all steps of the sequence.
- index : the order of the step which will be executed next time.
- results : an array for all results of each step.
Example
new Sequence( [
() => Promise.resolve(),
() => {
// this function will execute 1s after the previous one finished.
return true;
}
], {
autorun : true,
interval : 1000
} )
To append new steps into the sequence.
Parameters
-
steps
Function
|Iterable
The steps that you want to append to the sequence, each step must be a
Function
and should return a Promise object, if the return value of a step is not a Promise object, it will be converted to a resolved Promise object as its value is the return value.
Example
const sequence = new Sequence();
sequence.append( () => {
return request( url );
} );
To remove all steps in the sequence no matter if the steps have executed.
To run the next step in ths sequence if the sequence is not running. It cannot use while the sequence is running.
If there is a step in process while calling this method, the sequence will not go to the next step.
Return value
If the method execute successfully, a resolved Promise object of the step will be returned, otherwise a rejected one will be returned.
But in different situations, there would be some different result:
- When this method is called, if there is a step in process, the method will return the Promise object of the running step.
- When this method is called while the sequence is running, a rejected Promise instance will be returned, and the reason the the instance will be a
Sequence.Error
instance witherrno
is2
. - When this method is called while the sequence already reached the end, a rejected Promise instance will be returned, and the reason the the instance will be a
Sequence.Error
instance witherrno
is1
.
Example
const sequence = new Sequence( steps, { autorun : false } );
sequence.next().then( result => {
} );
To jump to a specified This method just set the cursor of the sequence to the previous one and it will not
Parameters
- index
Integer
The index of step to jump to.
Example
const sequence = new Sequence( steps );
sequence.on( 'failed', ( data, index ) => {
sequence.go( index - 1 ); // will re-execute this step.
} );
Notice that, by listening to the failed
event, cannot pend the sequence, so you cannot do some async things before you decide if you need to jump to one step.But maybe you can use it with stop
method. For example:
const sequence = new Sequence( steps );
sequence.on( 'failed', ( data, index ) => {
sequence.stop();
setTimeout( () => {
sequence.go( index - 1 );
sequence.run();
}, 1000 )
} );
To run the sequence if it is not running.
To stop the sequence if it is running
To suspend the sequence with specfiying a time then the sequence will be paused for a while and continue executing after the duration you set. During the time the sequence suspended, the sequence is still running.
Parameters
-
duration
Integer
- Default : 1000The duration you want to suspended the sequence, its unit is
ms
.
Example
const sequence = new Sequence( steps );
sequence.on( 'success', ( data, index ) => {
if( index % 10 ) {
sequence.suspend( 5000 );
}
} );
To run all steps in a sequence, the sequence will return a resolved Promise instance after all steps finished. If one of the steps failed, the function will return a rejected Promise, and if the steps is empty, the function will return a resolved Promise.
The param interval
is used for denoting the interval between each two steps.
The param callback
will be executed after creating the instance of Sequence
, so you can control the instance of Sequence
with this param.
The value of the resolved promise will be an Array
filled with result of each step.
Sequence.all( [
() => Promise.resolve( 1 ),
() => true
] ).then( results => {
// result list of the sequence
// some code...
} ).catch( results => {
// result list of the sequence
// some code...
} );
Sequence.all( [], sequence => {
sequence.on( 'success', () => {} );
} );
To run all steps in a sequence, and doesn't care about if all of the steps succeeded, a Promise
will be returned, and its value will be a full list of the results of the sequence, and if the steps is empty, a resolved Promise will be returned directly.
Sequence.chain( [
() => Promise.resolve( 'success' ),
() => Promise.reject( 'failed' )
] ).then( results => {
// result list of the sequence
// some code...
} );
To run the steps in sequence until one of them succeeded and a resolved Promise object will be returned. If all the steps executed failed, a rejected Promise object will be returned, and if the steps is empty, a rejected Promise will be returned.
Sequence.any( [
() => Promise.reject(),
() => Promise.resolve( true ),
() => {
// this function will not execute because the previous one succeeded.
return true;
}
] ).then( results => {
// result list of the sequence
// some code...
} ).catch( results => {
// result list of the sequence
// some sode...
} );
An error class for sequences.
const sequence = new Sequence( [] );
sequence.next().catch( e => {
if( e instanceof Sequence.Error ) {
// some code...
}
} );