async/timing.js

/**
 * Asynchronous tools.
 *
 * @module async
 * @license Apache-2.0
 * @copyright Mat. 2018-present
 */

import { identity } from "../func/tools";
import { timeUnit } from "../utils/misc";




/**
 * Delay current async execution by `time` miliseconds.
 *
 * Example:
 *
 * ```
 * (async () => {
 *     await async.delay()
 *     console.log("Hello ...")
 *     await async.delay()
 *     console.log("... world")
 * })()
 * ```
 *
 * @async
 * @function delay
 * @see [timeout]{@link module:async~timeout}
 * @param {Number} [time=timeUnit.second]
 * @param {Function} [passCancel] cancel => any
 * @returns {Promise.<Number>}
 */
export const delay = (
    time = timeUnit.second,
    passCancel = identity,
) =>
    timeout(() => time, time, passCancel);




/**
 * `setInterval` in `Promise` / `async` skin.
 *
 * Example 1:
 *
 * ```
 * global.x = 0
 * async.interval(
 *     c => {
 *         if (global.x === 10) { c("tada") }
 *         else { global.x += 1 }
 *         console.log("ping", Date.now())
 *     }
 * )
 * .then(utils.to_("success"))
 * .catch(utils.to_("failure"))
 * ```
 *
 * Example 2:
 * ```
 * interval(
 *     () => { console.log("Hey!"); return 42 },
 *     c => timeout(() => c(), 4 * timeUnit.second)
 * )
 * .then(x => console.log("Finished:", x))
 * .catch(c => console.log("Error:", c))
 * ```
 *
 * @async
 * @function interval
 * @param {Function} f
 * @param {Function} [passClear] (Function) => any
 * @param {Number} [time=timeUnit.second]
 * @returns {Promise.<any>}
 */
export const interval = (
    f,
    passClear = identity,
    time = timeUnit.second,
) => {
    let resolve = null, handle = null, result = null;
    const
        clear = (...args) => {
            clearInterval(handle);
            resolve(...(args.length > 0 ? args : [result]));
            return result;
        },
        promise = new Promise((res, rej) => {
            resolve = res;
            handle = setInterval(() => {
                try { result = f(clear); }
                catch (ex) {
                    clearInterval(handle);
                    rej(ex);
                }
            }, time);
        });

    passClear(clear);

    return promise;
};




/**
 * `setTimeout` in `Promise` / `async` skin.
 *
 * Example:
 *
 * ```
 * async.timeout(
 *     () => { console.log("Hey!"); return 42 }, 2000,
 *     c => async.timeout(() => c("Cancelled!"), 1000)
 * )
 * .then(x => console.log("Success:", x))
 * .catch(c => console.log("Error or cancel:", c))
 * ```
 *
 * @async
 * @function timeout
 * @param {Function} f
 * @param {Number} [time=timeUnit.second]
 * @param {Function} [passCancel] cancel => any
 * @returns {Promise.<any>}
 */
export const timeout = (
    f,
    time = timeUnit.second,
    passCancel = identity,
) => {
    let reject = null, handle = null;
    const promise = new Promise((res, rej) => {
        reject = rej;
        handle = setTimeout(() => {
            try { res(f()); }
            catch (ex) { rej(ex); }
        }, time);
    });

    passCancel(reason => {
        clearTimeout(handle);
        reject(reason);
    });

    return promise;
};