openpromise

2.3.3 • Public • Published

openpromise

The goal is of this module is to simplify and extend the use of the native Promise mechanism. It includes various objects that can be used to control the flow of a program or communicate between different parts of a program asynchronously. These objects leverage the native promise mechanism while avoiding the convoluted syntax of Promise definition. All objects are or behave like Promises, can be chained like Promises and allow the use of the then , catch and finally construct

The objects provided are:

OpenPromise It is the base object in the module; it extends the native ** Promise** by making it possible to resolve it remotely, e.g., outside its body. Its use instead of the native version makes the code much clearer.
Defer It is synonymous withOpenPromise and is included for compatibility with an older version of the module.
Delay is a Promise that resolves after a set time (it replaces the use of the setTimeout function)
TimeOut is a Promise that fails after a set time (it replaces the use of the setTimeout function)
Queue provides an execution queue for functions and Promises
Cycle is a Promise-like object that can be retriggered multiple times and remotely. It can be used to communicate or trigger events asynchronously between different parts of a program.
Repeater is Promise-like object that retriggers itself after an interval of time, it can be used instead of the setInterval function
Flipflop Promise-like object acting as an on/off gate that and holding code execution
untilResolved Function repeatedly trying to obtain a resolved Promise from a Promise returning function. It can be used to call multiple times an async function until successful; for example, to request a system resource

1. OpenPromise/Defer

Usage

The constructor has no argument: new OpenPromise() or new Defer()

The constructor returns an object that is an actual Promise object that can be used in every way a Promise can be used.

The key methods are :

resolve(v) to pass a value and trigger execution of the then code.
reject(v) to pass a value and trigger rejection of the catch code.
fail(v) synonymous with reject(v)
then(f) code to be executed after resolution (native promise method)
catch(f) code to be executed after rejection (native promise method)
finally(f) code to be executed after resolution or rejection (native promise method)

The object also exposed the properties.

resolved is true is the OpenPromise has been resolved otherwise it is undefined
rejected is true is the OpenPromise has been rejectd otherwise it is undefined

resolved

Please note that a rejected OpenPromise requires a catch(f) clause otherwise an unhandled promise rejection error will be generated (because an OpenPromise is a real Promise!)

Obviously, any code written using an OpenPromise could be written with a regular Promise; however, the normal Promise syntax requires to embedded the resolution/rejection code within the code creating the Promise. Such code can become very convoluted. With OpenPromise, the creation, resolution and rejection are separate codes.

Example 1

var opm= require("openpromise")
var df = new opm.Defer()   
df.then((val)=>{console.debug("received",val)})
df.then(()=>{console.debug("resolved?",df.resolved)})
df.then(()=>{console.debug("rejected?",df.rejected)})
console.debug("resolved?",df.resolved)
console.debug("rejected?",df.rejected)
df.resolve(45)

Ouput

resolved? undefined
rejected? undefined
received 45
resolved? true
rejected? undefined

Example 2

var opm= require("openpromise")
var df = new opm.Defer()

df.then((val)=>{console.debug("received",val)})
  .catch((val)=>{console.debug("failed",val)})
  .finally(()=>{console.debug("finally",)})
df.reject(99)

Output

failed 99
finally

2. Delay

Delay is a Promise that resolves itself after a delay. It can be used instead of the setTimeout function.

A Delay is an OpenPromise and thus also a Promise

                                                     
new opm.Delay(d, v) Constructor; d is the duration in milliseconds, v is the resolution value passed to the then. If d is omitted or if it is null or undefined, the duration is infinite and the Delay behaves like a regular OpenPromise (except that the duration can be set later with reset)
reset(d, val) Reinitializes the timer (only possible while it is still running)
resolve(v) Forces resolution before the end of duration (inherited from OpenPromise)
reject(v) Forces rejection before the end of duration (inherited from OpenPromise)
fail(v) Forces rejection before the end of duration (inherited from OpenPromise)
then(f) code to be executed after resolution (inherited from Promise)
catch(f) code to be executed after rejection (inherited from Promise)
finally(f) code to be executed after resolution or rejection (inherited from Promise)

The object also exposed the properties.

resolved is true is the OpenPromise has been resolved otherwise it is undefined (inherited from OpenPromise)
rejected is true is the OpenPromise has been rejectd otherwise it is undefined (inherited from OpenPromise)

Example 3

var opm= require("openpromise")
var dl = opm.Delay(1000,222)

dl.then((val)=>{console.debug("received",val)})
    .catch((val)=>{console.debug("failed",val)})
    .finally(()=>{console.debug("finally",)}))})

Output

received 222
finally

3. TimeOut

TimeOut is a Promise that fails itself after a delay (unless it has been resolved before). It can be used instead of the setTimeout function.

A TimeOut is an OpenPromise and thus also a Promise

                                                          
new opm.TimeOut(d, v) Constructor. d is the duration in milliseconds, v is the failure value passed to the catch. If d is omitted or if it is null or undefined, the duration is infinite and the TimeOut behaves like a regular OpenPromise (except that the duration can be set later with reset)
reset(d, val) Reinitializes the timer (only possible while it is still running)
resolve(v) Forces resolution before the end of duration (inherited from OpenPromise)
reject(v) Forces rejection before the end of duration (inherited from OpenPromise)
fail(v) Forces rejection before the end of duration (inherited from OpenPromise)
then(f) code to be executed after resolution (inherited from Promise)
catch(f) code to be executed after rejection (inherited from Promise)
finally(f) code to be executed after resolution or rejection (inherited from Promise)

The object also exposed the properties.

resolved is true is the OpenPromise has been resolved otherwise it is undefined (inherited from OpenPromise)
rejected is true is the OpenPromise has been rejectd otherwise it is undefined (inherited from OpenPromise)

Example 4

var opm= require("openpromise")
var to = new opm.TimeOut(1000,222)

to.then((val)=>{console.debug("received",val)})
    .catch((val)=>{console.debug("failed",val)})
    .finally(()=>{console.debug("finally",)})

Output

failed 222
finally

4. Queue

Queue is a very simple object that can be used to queue the execution of functions or promises. It is just a wrapper for an extensible chain of promise.then(f)

Its constructor takes no parameter and it has only one method enQueue(f)

enQueue(f) adds f to the FIFO execution queue . If f returns a Promise or f is a Promise , the queue will wait for the promise to be resolved or rejected.

enQueue(f) returns a Promise that is resolved or rejected when f completes. If f is a function, the ** Promise** resolves to the value returned by f or is rejected with the error code thrown by f .

If f is a Promise, the ** Promise** returned by enQueue(f) is resolved or rejected when f is resolved or rejected and with the same value.

The execution of the Queue continues normally, even if one step fails.

Example 5 (basic)

var opm= require("openpromise")
var qe = new opm.Queue()
qe.enQueue(()=>{console.debug("Do")})
qe.enQueue((s)=>{console.debug("Re")})
qe.enQueue(()=>{console.debug("Mi")})

Output

Do
Re
Mi

Example 6 (complex)

The following example shows how enQueue() accepts a function that returns a promise and also returns a promise.

var opm= require("openpromise")

var qe = new opm.Queue()

a = qe.enQueue(new opm.Delay(1000,1))
b = qe.enQueue(()=>{return new opm.TimeOut(1000,2)})
c = qe.enQueue(()=>{return 3})

a.then((x) => {console.log("success a",x)}).catch((x) => {console.log("failure a",x)})
b.then((x) => {console.log("success b",x)}).catch((x) => {console.log("failure b",x)})
c.then((x) => {console.log("success c",x)}).catch((x) => {console.log("failure c",x)})

new opm.Delay(1000,1)is a Promise that resolves to value 1 after one second. ()=>{return new opm.TimeOut(1000,2)} is a function that returns a Promise that rejects with value 2 after one second. ()=>{return 3}is a plain function that that return value 3 when executed

Output

[... one second wait ...]
success a 1   
[... one second wait ...]
failure b 2
success c 3

5. Cycle

Cycle is a Promise-like object that can be retriggered multiple times and remotely. It can be used to communicate or trigger events asynchronously between different parts of a program.

Usage

The constructor has no argument: new opm.Cycle()

Main methods :

                                            
new opm.Cycle() Constructor
thenAgain(f) Sets f to be executed asynchronously every time the Cycle is retriggered. thenAgain returns a OpenPromise/Defer that is resolved or rejected when the Cycle is terminated�
repeat(v) Re-triggers the cycle, v is passed to the excution functions
thenOnce(f) Set f to be executed asynchronously once the next time the Cycle is retriggered
terminate(f) Disables the Cycle, resolves the Cycle and resolves all OpenPromise/Defer created with thenAgain(f)
fail(f) Disables the Cycle , fails the Cycle and fails all OpenPromise/Defer created with thenAgain(f)

Other methods :

then(f) code to be executed after termination(inherited from Promise)
catch(f) code to be executed after failure (inherited from Promise)
finally(f) code to be executed after terminationor or failure (inherited from Promise)

The object also exposed the properties.

resolved is true is the OpenPromise has been terminated otherwise it is undefined (inherited from OpenPromise)
rejected is true is the OpenPromise has been failed otherwise it is undefined (inherited from OpenPromise)

Example 7

var opm= require("openpromise")
var cy = new opm.Cycle()
cy.thenAgain((val)=>{console.debug("received",val)})
cy.repeat(101)
cy.repeat(102)
cy.repeat(103))

Ouput

received 101
received 102
received 103

Termination

Each individual thenAgain(f) can be disabled separately, or the Cycle can be terminated as a whole.

Termination of an individual thenAgain(f)

The thenAgain(f) method returns an OpenPromise/Defer object. This object can be resolved by calling its resolve(v) method). Such resolution of the Defer/OpenPromise stops all further triggering by the repeat function; it can also trigger the execution of a final cleanup/finalization function that would have been established with a then(f) attached to that Defer/OpenPromise. Other instances of the thenAgain(func) will continue to be triggered.

Example 8

var opm= require("openpromise")
var cy = new opm.Cycle() 
var df = cy.thenAgain((val)=>{console.debug("received",val)}) 		// df is a Defer object.
df.then((val)=>{console.debug("the Defer is resolved with value",val)}) // to be executed after resolution
cy.repeat(1)
setTimeout(
  ()=>{df.resolve(99) 								// the Defer is resolved
  cy.repeat(2)
  },100)

The timer is needed to ensure that cy.repeat(1) is fully executed before df.resolve(99) is executed. It is not necessarily the case without the timer because cy.repeat(1) is executed asynchronously (see example 9 for behavior without the timer).

Output

received 1
the Defer is resolved with value 99

Termination of a Cycle

The Cycle object also has a terminate(val) method. This method disables the Cycle, and all future calls to repeat(v) will be ignored. Also, all Defer objects created with the thenAgain(func) will be resolved with the value passed to terminate.

Please note that a Cycle object is really an OpenPromise / Defer object that is resolved when it is terminated, so one can use the then construct to execute a finalization code just after a Cycle is terminated.

It is recommended to terminate every Cycle that is created to avoid memory leaks; otherwise, all the listening thenAgain will remain in memory. Only the Cycle needs to be terminated.

Example 9

var opm= require("openpromise")
var cy = new opm.Cycle()
cy.then((val)=>{console.debug("the Cycle is terminated with value",val)})
var df = cy.thenAgain((val)=>{console.debug("received",val)}) // df is a Defer object.
df.then((val)=>{console.debug("the Defer is resolved with value",val)}) // to be excuted after resolution
cy.repeat(1)
cy.terminate(99) // the Cycle is terminated
cy.repeat(2)

A timer is not necessary as in example 6 because calls to cy.repeat(val) and cy.terminate(val) are queued and are always executed asynchronously, but in the order, they were made.

Output

received 1
the Cycle is terminated with value 99
the Defer is resolved with value 99

Example 10 (complex)

var opm= require("openpromise")
var timer
var cy = new opm.Cycle()
var df1 = cy.thenAgain((val)=>{console.debug("1 received",val)})
df1.then((val)=>{console.debug("1 is terminated with value",val)})
var df2 = cy.thenAgain((val)=>{console.debug("2 received",val)})
df2.then((val)=>{console.debug("2 is terminated with value",val)})
var df3 = cy.thenAgain((val)=>{if(val>=3) {df1.resolve(100)}})
var df4 = cy.thenAgain((val)=>{if(val>=5) {
  cy.terminate(999)
  clearInterval(timer)
  }})

let n = 1
timer = setInterval(()=>{cy.repeat(n)
            n = n +1
            }
            ,100)

Ouput

1 received 1
2 received 1
1 received 2
2 received 2
1 received 3
2 received 3
1 is terminated with value 100
2 received 4
2 received 5
2 is terminated with value 999

Timing of execution

Calls to methods on a Cycle object can occur asynchronously anywhere in the code where this Cycle object is available. The only guarantee is that the execution of all cy.repeat(val), cy.terminate(val) andcy.fail(val) calls will be queued and executed successively in the order they were made. A call to df.resolve(val) (where df = cy.thenAgain(f)) is not queued and cy.thenAgain(f) can be resolved/disabled before all pending values from cy.repeat(val) are treated.

Example 11

var opm= require("openpromise")
var cy = new opm.Cycle()
var df = cy.thenAgain((val)=>{console.debug("received",val)}) 		// df is a Defer object.
df.then((val)=>{console.debug("the Defer is resolved with value",val)}) // to be excuted after resolution
cy.repeat(1)
df.resolve(99) 								// the Defer is resolved
cy.repeat(2)

Output

the Defer is resolved with value 99

Because cy.repeat(1) is queued and executed asynchronously, df is resolved before the value 1 is treated, and the value is ignored.

Failure

fail(val)

The fail(val) method of a Cycle object triggers the failure of all Defer objects derived from it with thenAgain(f). Since these Defer objects are Promises, failure can be caught with ``catch(f). A call tofail(val)also disables the Cycle, and all future calls torepeat(v)``` will be ignored.

Example 12

var opm= require("openpromise")

var cy1 = new opm.Cycle()
cy1
    .then((v)=>{console.debug("cy1 terminated",v)})
    .catch((v)=>{console.debug("cy1 failed",v)})

cy1.thenAgain((v)=>{console.debug("thenAgain1",v)})
    .then((v)=>{console.debug("then1",v)})
    .catch((v)=>{console.debug("catch1",v)})


cy1.repeat(10)
cy1.terminate(11)


var cy2 = new opm.Cycle()
cy2
    .then((v)=>{console.debug("cy2 terminated",v)})
    .catch((v)=>{console.debug("cy2 failed",v)})

cy2.thenAgain((v)=>{console.debug("thenAgain2",v)})
    .then((v)=>{console.debug("then2",v)})
    .catch((v)=>{console.debug("catch2",v)})

cy2.repeat(20)
cy2.fail(21)

Output

thenAgain1 10
thenAgain2 20
cy1 terminated 11
then1 11
cy2 failed 21
catch2 21

6. Repeater

Repeater is a Promise-like object that retriggers itself repeatedly after a set time. It can be used instead of the setInterval function. It is derived from Cycle and behaves in a similar fashion.

Main methods :

                                                   
new opm.Repeater(d,f) Constructor; d is the timer delay, f is an optional function whose value will be calculated at each cycle and passed as a resolution value
thenAgain(f) Sets f to be executed asynchronously every time the Repeater is retriggered. thenAgain returns a OpenPromise/Defer that is resolved or rejected when the Repeateris terminated
thenOnce(f) Set f to be executed asynchronously once the next time the Repeater is retriggered
terminate(f) Disables the Repeater, resolves the Repeater and resolves all OpenPromise/Defer created with thenAgain(f)
fail(f) Disables the Repeater , fails the Repeater and fails all OpenPromise/Defer created with thenAgain(f)

Other methods :

then(f) code to be executed after termination(inherited from Promise)
catch(f) code to be executed after failure (inherited from Promise)
finally(f) code to be executed after terminationor or failure (inherited from Promise)

The object also exposed the properties.

resolved is true is the OpenPromise has been terminated otherwise it is undefined (inherited from OpenPromise)
rejected is true is the OpenPromise has been failed otherwise it is undefined (inherited from OpenPromise)

Example 13

var n = 1

f = ()=>{return n++}
var rp = new opm.Repeater(500,f)
rp.thenAgain((v)=>{console.debug("received",v)})
rp.then(()=>{console.debug("terminated")})
dl = new opm.Delay(5000)
dl.then(()=>{rp.terminate()})

Output

received 1
received 2
received 3
received 4
received 5
received 6
received 7
received 8
received 9
terminated

7. Flipflop

Flipflop is a Promise-like object that can act as a resettable program gate that can be opened, closed, or flipped.

                                                          
new opm.Flipflop() Constructor
on(v) Resolve the object (open the gate)
off(v) Reset the object (close the gate)
flip(v) Switch state between opened and closed
fail(v) Reject/fail the object
then(f) code to be executed as soon as the object is on
catch(f) code to be executed in case of failure
finally(f) code to be executed as soon as the object is on or in case of failure

The object also exposed the properties.

resolved is true is the OpenPromise has been resolved otherwise it is undefined (inherited from OpenPromise)
rejected is true is the OpenPromise has been rejectd otherwise it is undefined (inherited from OpenPromise)

Example 14

var opm= require("openpromise")
  
var ff = new opm.Flipflop()
ff.then(()=>{console.debug("Hello 1")})

console.debug(">setting on")
ff.on()
console.debug(">flipping")
ff.flip()
ff.then(()=>{console.debug("Hello 2")})
console.debug(">flipping again")
ff.flip()
console.debug(">and setting off")
ff.off()
ff.then(()=>{console.debug("Hello 3")})

Output

>setting on
>flipping
>flipping again
>and setting off
Hello 1
Hello 2

Note that the execution is asynchronous

8. untilResolved

untilResolved returns an OpenPromise representing the result of repeatedly calling an async function until it returns a Promise that resolves successfully. A typical scenario would to repeatedly attempt to open a communication sockets or a file until success.

Practically, untilResolved returns OpenPromise and then calls the async function. It then waits for the Promise returned by the async function to be resolved or rejected. If the Promise from the async function is rejected, untilResolved calls the function again. If the obtained a Promise resolves successfully, the Promise untilResolved had return also resolves.

The call format is the following: untilResolved(f, n, d)

f The asynchronous function to be called, this function should return a Promise
n an optional integer representing the maximum number of attempts to be made. If omitted, there is no limit to the number of repetitions.
d an optional delay in milliseconds between successive calls. If omitted, there is no timeout.
return an OpenPromise representing the outcome of the process. The Promise resolves when a call to f succeeds and fails (is rejected) of n or d is exceeded.

Example 15

var opm= require("./openpromise.js")

// the following code creates a test function foo that for 2 seconds returns Promises that fail with 100 milliseconds, after which it returns promises that resolve successfully. 
  
var dl= new opm.Delay(2000)
var n = 1
foo = ()=>{console.debug(n++);
    return dl.resolved ? new opm.Delay(100) : new opm.TimeOut(100)}

 op = opm.untilResolved(foo,100,200)
 op 
     .then(()=>{console.debug("done")})
     .catch(()=>{console.debug("give up")})

9. Acknowledgment

The code of the Defer object is based on the Defer() function proposed by Carter in this post:

https://stackoverflow.com/questions/26150232/resolve-javascript-promise-outside-function-scope

Versions

Current Tags

  • Version
    Downloads (Last 7 Days)
    • Tag
  • 2.3.3
    125
    • latest

Version History

Package Sidebar

Install

npm i openpromise

Weekly Downloads

125

Version

2.3.3

License

ISC

Unpacked Size

34 kB

Total Files

4

Last publish

Collaborators

  • cabrouwers