Skip to content

Instantly share code, notes, and snippets.

@mr-moon
Created November 4, 2017 14:14
Show Gist options
  • Save mr-moon/d862d15ed250cb8ed6ef0a34a1fef859 to your computer and use it in GitHub Desktop.
Save mr-moon/d862d15ed250cb8ed6ef0a34a1fef859 to your computer and use it in GitHub Desktop.
clean javascript API for waiting for some condition to be met
/**
* A helper that simplifies waiting for something to happen. It acts like an observer without any
* particular observable. So your 'evaluator' function may do anything, untill it's returns non-undefined value
*
* For example, you may want to "observe" a sessionStorage value regardless of browser support of the
* "storage" events.
*
* @example
* ```js
* var awaiter = await(
* // Evaluator
* function() {
* return sessionStorage['myid'] === '1' || undefined;
* },
*
* // Callback
* function(id) {
* console.log('ok, is see my id in sessionStorage now, and it is (should be 1, isn\'t it?)' + id);
* },
*
* // how often (in milliseconds) I need to look at sessionStorage?
* 100,
*
* // Do I want to evaluate immediately?
* true);
*
* // If I need to limit expectation time, we can do it. Then value passed to callback function is undefined
* awaiter.expireAfter(15000); // 15 seconds
*
* // whenever I need to cancel it
* awaiter.cancel();
*
* ```
*
* Chaining supported too:
* ```js
* await(...).expireAfter(1000).cancel();
* ```
*
* @param {function} evaluatorFn An evaluator function, that should is executed every tick
* @param {function} callbackFn A callback function that is executed after evaluatorFn returns defined
* (!== undefined) value. It also gets the value that evaluatorFn has returned
* @param {number} interval A time period of time that should pass before next evaluation, in ms
* @param {Boolean} [trailing=false] Flag that specifies if initial evaluation happens immediately or after a delay.
* true - evaluate first only after the delay, false - evaluate immediately
* @return Awaiter Returns evaluation processing function. Exposes 'cancel()' and `expireAfter()`.
* @author Alexander Moon <alex@tacticrealtime.com>
*/
function await(evaluatorFn, callbackFn, interval, trailing) {
var result, tickFn, expiresAt;
if (!trailing && (result = evaluatorFn()) !== undefined) {
callbackFn(result);
return /** @type Awaiter */ {cancel: noop, expireAfter: noop};
}
/**
* @typedef {function} AwaiterExpireAfter
* @param {number} ms Specify when to expire. In milliseconds.
* @return Awaiter
*/
/**
* @typedef {function} AwaiterCancel
* @return undefined
*/
/**
* @name Awaiter
* @type {function} Awaiter
* @property {AwaiterCancel} cancel Cancels the wait
* @property {AwaiterExpireAfter} expireAfter Sets the time limit for the awaiter
*/
tickFn = function evaluationTickFn() {
var result = evaluatorFn();
if (result !== undefined) {
tickFn.cancel();
tickFn = undefined;
callbackFn(result);
}
if (expiresAt && expiresAt > new Date().getTime()) {
callbackFn(undefined);
tickFn.cancel();
}
};
tickFn.cancel = function () {
clearInterval(tickFn._intervalId);
tickFn._intervalId = undefined;
result = undefined;
tickFn.cancel
= tickFn.expireAfter
= function () {
};
};
tickFn.expireAfter = function (ms) {
expiresAt = new Date(new Date().getTime() + ms).getTime();
return tickFn;
};
tickFn._intervalId = setInterval(function () {
//noinspection JSUnresolvedFunction
tickFn.call(tickFn);
}, interval);
return tickFn;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment