AsyncTimeout: Now handles async functions for `thing` correctly. Improved `timeout_example(_timeout, cancel)` (+ `Cancellation` class for async cancellations). Added `try_example(time)` which runs the default example and cancels after `time`, returning the value or error in an object containing either `{ value: true }` or `{ error: <caught error> }` when awaited.

Added AsyncTimeout.await_timeout(time): Returns a promise that resolves when `time` has passed. No value is returned from this promise.

Added AsyncTimeout.await_interval(time): Returns an async iterator that yields after `time` has passed. No value is yielded from this async iterator.

(These helper functions are essentially the same as `new AsyncTimeout(()=>{}, time).[timeout/interval]()`.)

Fortune for async-timeout-js's current commit: Middle blessing − 中吉
allow-await-async-timeout-func
Avril 2 years ago
parent 0d9f2adb04
commit d6f9ff7817
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -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()`.
///
/// # `setTimeout()`
@ -15,43 +25,128 @@
/// See `examples` below.
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) => {
setTimeout(() => {
try {
_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...
} catch(e) { reject(e); }
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(() => {
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);
});
}
};
}
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;
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());
// Continuously wait 100 then alert "hi"
for await (const value of new AsyncTimeout(() => "hi forever", _timeout).interval()) { console.log(value); }
// 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 }`

Loading…
Cancel
Save