Last active
May 4, 2025 01:29
-
-
Save kumavis/13cce0056f17bff0bf39cc85d79cd1dc to your computer and use it in GitHub Desktop.
LazyPromise
This file contains hidden or 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
/** | |
* A LazyPromise doesn't run its executor until .then, .catch, or .finally is called, | |
* or the promise is awaited. | |
* Unfortunately, `@endo/promise-kit`'s isPromise returns false for instances of LazyPromise. | |
* However, LazyPromise instances will return true for instanceof Promise. | |
*/ | |
export class LazyPromise extends Promise { | |
#isListening = false; | |
#executor; | |
#resolve; | |
#reject; | |
static get [Symbol.species]() { | |
return Promise; | |
} | |
constructor(executor) { | |
let resolve; | |
let reject; | |
super((res, rej) => { | |
resolve = res; | |
reject = rej; | |
}); | |
this.#executor = executor; | |
this.#resolve = resolve; | |
this.#reject = reject; | |
} | |
#setListening() { | |
if (this.#isListening) return; | |
this.#isListening = true; | |
this.#executor(this.#resolve, this.#reject); | |
} | |
then(onFulfilled, onRejected) { | |
this.#setListening(); | |
return super.then(onFulfilled, onRejected); | |
} | |
catch(onRejected) { | |
this.#setListening(); | |
return super.catch(onRejected); | |
} | |
finally(onFinally) { | |
this.#setListening(); | |
return super.finally(onFinally); | |
} | |
} | |
harden(LazyPromise); |
This file contains hidden or 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
import test from '@endo/ses-ava/prepare-endo.js'; | |
import { isPromise, makePromiseKit } from '@endo/promise-kit'; | |
import { LazyPromise } from '../src/lazy-promise.js'; | |
test('isPromise is false (sad but true)', t => { | |
const promise = new LazyPromise(() => {}); | |
t.is(isPromise(promise), false); | |
}); | |
test('instanceof Promise', t => { | |
const promise = new LazyPromise(() => {}); | |
t.is(promise instanceof Promise, true); | |
}); | |
test('.then before resolve', t => { | |
let executorCalled = false; | |
let resolve; | |
const promise = new LazyPromise(res => { | |
resolve = res; | |
executorCalled = true; | |
}); | |
t.is(executorCalled, false); | |
promise.catch(() => {}); | |
t.is(executorCalled, true); | |
resolve(); | |
t.is(executorCalled, true); | |
}); | |
test('.catch before resolve', t => { | |
let executorCalled = false; | |
let resolve; | |
const promise = new LazyPromise(res => { | |
resolve = res; | |
executorCalled = true; | |
}); | |
t.is(executorCalled, false); | |
promise.catch(() => {}); | |
t.is(executorCalled, true); | |
resolve(); | |
t.is(executorCalled, true); | |
}); | |
test('.finally before resolve', t => { | |
let executorCalled = false; | |
let resolve; | |
const promise = new LazyPromise(res => { | |
resolve = res; | |
executorCalled = true; | |
}); | |
t.is(executorCalled, false); | |
promise.finally(() => {}); | |
t.is(executorCalled, true); | |
resolve(); | |
}); | |
test('await', async t => { | |
let executorCalled = false; | |
let resolve; | |
const promise = new LazyPromise(res => { | |
resolve = res; | |
executorCalled = true; | |
}); | |
t.is(executorCalled, false); | |
// Make a normal promise kit to control the async function | |
const { promise: readyP, resolve: ready } = makePromiseKit(); | |
// Kick off an async function that will await the lazy promise | |
(async () => { | |
ready(); | |
await promise; | |
})(); | |
// Wait for the async function to start | |
await readyP; | |
t.is(executorCalled, true); | |
resolve(); | |
}); | |
test('.catch before reject', t => { | |
let executorCalled = false; | |
let reject; | |
const promise = new LazyPromise((res, rej) => { | |
reject = rej; | |
executorCalled = true; | |
}); | |
t.is(executorCalled, false); | |
promise.catch(() => {}); | |
t.is(executorCalled, true); | |
reject(); | |
t.is(executorCalled, true); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment