@ -1,3 +1,13 @@
const is _promise = obj => ! ! obj && ( typeof obj === 'object' || typeof obj === 'function' ) && typeof obj . then === 'function' ;
const fire _and _forget = ( a , on _error ) => {
( async ( ) => {
try {
await a ( ) ;
} catch ( e ) { if ( on _error ) on _error ( e ) ; else throw ( e ) ; }
} ) ( ) ;
} ;
/// An async wrapper around `setTimeout()` and `setInterval()`.
/// An async wrapper around `setTimeout()` and `setInterval()`.
///
///
/// # `setTimeout()`
/// # `setTimeout()`
@ -15,43 +25,128 @@
/// See `examples` below.
/// See `examples` below.
function AsyncTimeout ( thing , interval )
function AsyncTimeout ( thing , interval )
{
{
function isPromise ( obj ) {
return ! ! obj && ( typeof obj === 'object' || typeof obj === 'function' ) && typeof obj . then === 'function' ;
}
async function _call ( t )
{
if ( isPromise ( t ) ) return await t ( arguments ) ;
else await ( async ( ) => { } ) ;
return t ( arguments ) ;
}
this . timeout = ( ) => new Promise ( ( resolve , reject ) => {
this . timeout = ( ) => new Promise ( ( resolve , reject ) => {
setTimeout ( ( ) => {
setTimeout ( ( ) => {
try {
const eval _thing = ( ) => {
_call ( thing ) . then ( resolve ) ; //XXX: This design doesn't work, we'll need to go back to the drawing board with this function for this to work... The exception won't propagate here, so...
try {
} catch ( e ) { reject ( e ) ; }
return thing ( ) ;
} catch ( e ) { reject ( e ) ; }
} ;
const rv = eval _thing ( ) ;
if ( is _promise ( rv ) ) fire _and _forget ( async ( ) => resolve ( await rv ) , reject ) ;
else resolve ( rv ) ;
} , interval ) ;
} , interval ) ;
} ) ;
} ) ;
this . interval = async function * ( ) {
this . interval = async function * ( ) {
while ( true ) {
while ( true ) {
yield await new Promise ( ( resolve , reject ) => {
yield await new Promise ( ( resolve , reject ) => {
const eval _thing = ( ) => {
try {
return thing ( ) ;
} catch ( e ) { reject ( e ) ; }
} ;
setTimeout ( ( ) => {
setTimeout ( ( ) => {
try { _call ( thing ) . then ( resolve ) ; } catch ( e ) { reject ( e ) ; }
const irv = eval _thing ( ) ;
if ( is _promise ( irv ) ) fire _and _forget ( async ( ) => resolve ( await irv ) , reject ) ;
else resolve ( irv ) ;
} , interval ) ;
} , interval ) ;
} ) ;
} ) ;
}
}
} ;
} ;
}
}
const example = async ( _timeout ) => {
/// Return a promise that will resolve when the timeout `i` has completed. There is no value for this resolution.
AsyncTimeout . await _timeout = ( i ) => new AsyncTimeout ( ( ) => { } , i ) . timeout ( ) ;
/// Return an async iterator that will resolve the next iteration every `i`. There is no value for these yielded resolutions.
AsyncTimeout . await _interval = ( i ) => new AsyncTimeout ( ( ) => { } , i ) . interval ( ) ;
/// Cancellation token source for `timeout_example`
function Cancellation ( ) {
let tokens = [ ] ;
this . cancel _after _interrupt = ( sig ) => new Promise ( resolve => {
sig = sig || 'SIGINT' ;
process . on ( sig , ( ) => {
//console.log(`>>> Received signal ${sig}`);
resolve ( sig || "<signal>" ) ;
} ) ;
} ) ;
this . cancel _after _invoke = ( ) => new Promise ( resolve => {
tokens . push ( resolve ) ;
} ) ;
this . cancel _invoke = ( value ) => tokens . shift ( ) ( value || "<invoked>" ) ;
}
/// Run the timeout example
const timeout _example = async ( _timeout , cancelAfter ) => {
_timeout = _timeout || 100 ;
_timeout = _timeout || 100 ;
let cancel = false ;
const canceller = new Promise ( resolve => {
if ( is _promise ( cancelAfter ) ) ( async ( ) => {
resolve ( cancel = ( ( await cancelAfter ) || true ) ) ;
} ) ( ) ;
else if ( cancelAfter && cancelAfter > 0 ) setTimeout ( ( ) => resolve ( cancel = true ) , cancelAfter ) ;
else resolve ( false ) ;
} ) ;
// Wait 100 then alert "hi"
// Wait _timeout then alert "hi"
console . log ( await ( new AsyncTimeout ( ( ) => "hi" , _timeout ) ) . timeout ( ) ) ;
console . log ( await ( new AsyncTimeout ( ( ) => "hi" , _timeout ) ) . timeout ( ) ) ;
// Continuously wait 100 then alert "hi"
// Wait _timeout then call then await this async lambda, then print its result.
for await ( const value of new AsyncTimeout ( ( ) => "hi forever" , _timeout ) . interval ( ) ) { console . log ( value ) ; }
console . log ( await ( new AsyncTimeout ( async ( ) => {
console . log ( "> Hi starting promise..." ) ;
await AsyncTimeout . await _timeout ( _timeout ) ;
console . log ( ">> Hi inside promise!" ) ;
await AsyncTimeout . await _timeout ( _timeout ) ;
console . log ( "> Hi ending promise..." ) ;
return "hi, from async lambda!" ;
} , _timeout ) ) . timeout ( ) ) ;
// Wait _timeout as with this async lambda which uses `.interval()` producing the result `(_timeout / 10) || 10` times.
console . log ( await ( new AsyncTimeout ( async ( ) => {
let i = 0 ;
const len = ( _timeout / 10 ) || 10 ;
for await ( const value of AsyncTimeout . await _interval ( _timeout ) ) {
console . log ( ` [al]: iteration ${ i } ` ) ;
i += 1 ;
if ( cancel ) throw "Operation cancelled." ;
else if ( i == len ) return ` Hi from async lambda containing iterator!, Completed ${ i } iterations ` ;
}
} , _timeout ) ) . timeout ( ) ) ;
// Wait _timeout as interval running at most `(_interval / 10) || 10` times
console . log ( await ( async ( ) => {
let i = 0 ;
const len = ( _timeout / 10 ) || 10 ;
for await ( const value of new AsyncTimeout ( async ( ) => {
console . log ( ` i > Waiting ${ _timeout * 2 } ` ) ;
await AsyncTimeout . await _timeout ( _timeout * 2 ) ;
console . log ( ` i > Returning ${ i } as iteration value. ` ) ;
return ` ${ i } ` ;
} , _timeout ) . interval ( ) ) {
console . log ( ` !i >>> Iteration ${ i } ` ) ;
i += 1 ;
if ( cancel ) throw "Operation cancelled." ;
else if ( i == len ) return value ;
}
} ) ( ) ) ;
// Continuously wait _timeout then alert "hi forever", until `cancelAfter` is reached (if it is)
for await ( const value of new AsyncTimeout ( ( ) => "hi forever" , _timeout ) . interval ( ) ) { if ( cancel ) break ; console . log ( value ) ; }
// Return `true` if cancelled (or, if `cancelAfter` was a promise that returned a truthy value, that resolved value); `false` if not (should never happen)
return await canceller ;
} ;
} ;
/// Run the timeout example, catching an error if there is one thrown (usually on cancellation.)
const try _example = async ( time ) => {
try { return { value : await timeout _example ( null , time ) } ; }
catch ( e ) { console . log ( ` Error! ${ e } ` ) ; return { error : e } ; }
} ;
//try_example(2500); // Error! Operation cancelled. -> `{ error: "Operation cancelled." }`
//try_example(5000); // -> `{ value: true }`