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()`.
///
/// # `setTimeout()`
/// `await (new AsyncTimeout(() => "value", 100)).timeout() /* === "value" */` is `setTimeout(()=> "value", 100)` promisified.
///
/// # `setInterval()`
/// `for await (const value of new AsyncTimeout(() => "value", 100)).interval()) { /* value === "value" */ }` is `setInterval(()=> "value", 100)` promisified.
/// This is an infinite iterator. To cancel the interval, simply break out of the `for await` loop.
///
/// # Parameters
/// * `thing` - The function to call after the timeout/interval. The result of the promise (or the yield for `interval()`) is the result of this function call. If the funtion throws, then the rejection of the promise will be that error thrown.
/// * `interval` - The time to wait for the timeout or interval
///
/// # Examples
/// See `examples` below.
function AsyncTimeout ( thing , interval )
{
this . timeout = ( ) => new Promise ( ( resolve , reject ) => {
setTimeout ( ( ) => {
const eval _thing = ( ) => {
try {
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 ) ;
} ) ;
this . interval = async function * ( ) {
while ( true ) {
yield await new Promise ( ( resolve , reject ) => {
const eval _thing = ( ) => {
try {
return thing ( ) ;
} catch ( e ) { reject ( e ) ; }
} ;
setTimeout ( ( ) => {
const irv = eval _thing ( ) ;
if ( is _promise ( irv ) ) fire _and _forget ( async ( ) => resolve ( await irv ) , reject ) ;
else resolve ( irv ) ;
} , interval ) ;
} ) ;
}
} ;
}
/// 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 ;
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 _timeout then alert "hi"
console . log ( await ( new AsyncTimeout ( ( ) => "hi" , _timeout ) ) . timeout ( ) ) ;
// Wait _timeout then call then await this async lambda, then print its result.
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 }`