Reputation: 7003
Trying to come up with API, mixing in promise functionality like:
class Awaitable {
constructor () {
this.promise = Promise.resolve()
}
then (fn) {
// awaited result must be _this_ instance
return this.promise.then(() => fn(this))
}
}
let smth = await (new Awaitable())
console.log(smth)
This code creates recursion.
The main point is to have smth
to be the newly created thenable instance.
Stubbing then
with null
makes awaited result incomplete.
I wonder if that's possible at all, seems like there's some conceptual hurdle, I can't wrap my head around.
Upvotes: 1
Views: 423
Reputation: 7003
Symbol.thenable proposal.
import { parse as parseStack } from 'stacktrace-parser'
class Awaitable {
constructor () {
this.promise = Promise.resolve()
},
[Symbol.thenable]: false,
then(fn) {
this.promise.then(() => fn(this))
return this
}
}
let smth = await (new Awaitable())
console.log(smth.then) // function
Detecting if thenable
instance is called by await
is possible by parsing callstack. Here's solution based on stacktrace-parser package:
import { parse as parseStack } from 'stacktrace-parser'
class Awaitable {
constructor () {
this.promise = Promise.resolve()
}
}
Object.defineProperty(Awaitable.prototype, 'then', {
get() {
let stack = parseStack((new Error).stack)
// naive criteria: if stacktrace is leq 3, that's async recursion, bail out
// works in webkit, FF/nodejs needs better heuristic
if (stack.length <= 3) return null
return (fn) => {
this.promise.then(() => {
fn(this)
})
return this
}
}
})
let smth = await (new Awaitable())
console.log(smth.then) // function
The heuristic must be enhanced for FF/nodejs, to solidify - that'd require sort of static analysis wizardy.
Upvotes: -1
Reputation: 665276
It makes no sense for a promise (or any thenable) to fulfill with itself. If you already had the object, you wouldn't need to wait for it.
Resolving a promise does automatically unwrap thenables, which would create infinite recursion in your case where it would resolve with itself, and you can (unfortunately) not avoid that. So just don't attempt to do it. Fulfill your promise with nothing (undefined
) instead.
class Awaitable {
constructor () {
this.promise = Promise.resolve(undefined); // simplified
}
then(onfulfill, onreject) {
return this.promise.then(onfulfill, onreject);
}
// other methods
}
const smth = new Awaitable();
await smth; // works just fine now
console.log(smth);
Upvotes: 0