Created
April 6, 2022 14:47
-
-
Save SimonMeskens/746e011500b2ffe4058299bf222b42b0 to your computer and use it in GitHub Desktop.
A lazy promise that only tries to resolve itself when prompted.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @module | |
* @copyright Simon Meskens 2022 | |
* @license | |
* Copying and distribution of this file, with or without modification, | |
* are permitted in any medium without royalty, provided the copyright | |
* notice and this notice are preserved. This file is offered as-is, | |
* without any warranty. | |
*/ | |
/** | |
* @template R, [T=never] | |
* @callback OnResult | |
* @param {T} [value] | |
* @returns {R | PromiseLike<R>} | |
*/ | |
/** | |
* @template [T=*] | |
* @callback Finish | |
* @param {T | PromiseLike<T>} value | |
* @returns {void} | |
*/ | |
/** | |
* Represents a lazy promise that only tries to resolve itself when accessed. | |
* @template T | |
* @extends Promise<T> | |
*/ | |
export class Voucher extends Promise { | |
static [Symbol.species]() { | |
return Promise; | |
} | |
[Symbol.toStringTag]() { | |
return "Voucher"; | |
} | |
/** | |
* The evaluator function. | |
* @type {OnResult<T>} | |
*/ | |
#evaluate; | |
/** | |
* Resolves the Voucher. | |
* @type {Finish<T>} | |
*/ | |
#resolve; | |
/** | |
* Rejects the Voucher. | |
* @type {Finish} | |
*/ | |
#reject; | |
/** | |
* Has the Voucher been redeemed yet? | |
* @type {boolean} | |
*/ | |
#done = false; | |
/** | |
* Constructs a new {@link Voucher}. | |
* @constructor | |
* @param {OnResult<T>} evaluator | |
* The evaluation function that runs on resolve. | |
*/ | |
constructor(evaluator) { | |
/** @type {{ resolve: Finish<T>, reject: Finish }} */ | |
let args; | |
super((resolve, reject) => { | |
args = { resolve, reject }; | |
}); | |
this.#resolve = args.resolve; | |
this.#reject = args.reject; | |
this.#evaluate = evaluator; | |
} | |
/** | |
* Evaluates the evaluator and returns the generated promise. | |
* @async | |
* @returns {Promise<T>} | |
* The generated promise. | |
*/ | |
async redeem() { | |
if (this.#done) throw new Error("Can't execute Lazy twice."); | |
return Promise.resolve(this.#evaluate()).then(this.#resolve, this.#reject); | |
} | |
/** | |
* Attaches callbacks for the resolution and/or rejection of the Promise. | |
* @override | |
* @template [R1=T], [R2=never] | |
* @param {OnResult<R1, T>} [onfulfilled] | |
* The callback to execute when the Promise is resolved. | |
* @param {OnResult<R2, any>} [onrejected] | |
* The callback to execute when the Promise is rejected. | |
* @returns {Promise<R1 | R2>} | |
* A Promise for the completion of which ever callback is executed. | |
*/ | |
then(onfulfilled, onrejected) { | |
if (!this.#done) this.redeem(); | |
return super.then(onfulfilled, onrejected); | |
} | |
} | |
/** | |
* Creates a new {@link Voucher}. | |
* @template T | |
* @param {OnResult<T>} evaluator | |
* The evaluation function that runs on resolve. | |
* @returns {Voucher<T>} | |
* A voucher of the return type of the evaluator. | |
*/ | |
export const voucher = evaluator => | |
Promise.resolve({ | |
then: (...args) => Promise.resolve(evaluator()).then(...args) | |
}); | |
export default Voucher; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment