-
Notifications
You must be signed in to change notification settings - Fork 7
Alternative names for withResolvers()
#2
Comments
That's called a "deferred", colloquially, and in jQuery.
|
I've always called these "deferreds", so I'd be happy with |
That makes sense, but I assumed the name |
this mirrors |
Promises are a subset of Deferreds, so i'm not sure distance is required. |
My reasoning for avoiding Whatever we call it, users who already know this method as My issue with |
|
Why it has to be a static method? Why not change the executor to be optional? Shouldn't this much easier to use? const promise = new Promise;
promise.resolve(1);
console.log(await promise); // polyfill const LegacyPromise = globalThis.Promise;
globalThis.Promise = class Promise extends LegacyPromise {
constructor(executor) {
super (
executor ?? (resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
}
);
}
} |
@fisker that would both mask and create bugs; it would be a very bad idea to accidentally expose a promise's resolvers. |
I'm assuming you means twisted's deferred here, in which case it's the exact same thing as we're proposing here. It's not a promise, but a promise and its resolve/reject (called
I think this isn't a great argument to break with a naming precedent. What is a "promise"? Well developers didn't know what it was until they learned what it was. And what's a "deferred"? They'll learn it when they learn it, unless they already know it because it's a popular name for an existing pattern. |
Promise.withResolvers() // Object {promise, resolve, reject}
new Promise // Promise {resolve, reject} What's the difference? |
@fisker |
How do you think? const promise = Promise.withResolvers();
promise.resolve(1); That's a real "with resolvers", since resolvers are assigned to promise. 😄 |
@fisker i'm not sure what you're arguing. Specifically, when typing |
How about accept an option? const promise = new Promise({
defer: true,
// Or
exposeResolvers: true,
});
// Or
const promise = Promise.create({exposeResolvers: true})
promise.resolve(1);
console.log(await promise); |
That would certainly be a viable alternative. |
|
first option is a non-starter because the 2nd option has cognitive friction (for me anyway) due to more of an aside, but naming the wrapper object variable |
per #1 it seems more and more like we are necromancing |
The current proposal has it that there's no interface PromisePlusItsResolvers<T> {
promise: Promise<T>
resolve: (value: T) => void
reject: (reason?: string) => void
}
class Promise {
static withResolvers<T>(): PromisePlusItsResolvers<T>;
}
It's not much of a precedent -- there's no precedent in the spec, nor is it something widespread in other PLs. It exists in the ecosystem but even there Deferreds are not exactly the same thing as what
Promises were a new primitive that needed a concise name, and "promise" is highly evocative of what they do. Agreed that developers were never going to be able to intuit what they were from just their name and you're right that that's okay in that case. But that's not the situation here. This is just a POJO that contains a promise along with its resolve and reject functions. We don't really need a concise name for that thing because 99 times out of 100 people are going to just destructure it anyway. |
The spec calls these "capabilities", which suggests |
Yes, they are slightly different because Twisted doesn't have a promise object and we store the the
An impl in the most popular language library in the ecosystem. And in the precursor to the Promise spec, and the most famous promise library, a non-standard implementation in Chrome, internal names in projects like TS and node, official APIs in Deno. There is a naming precedent in our ecosystem, and even if the the exact object has slightly different properties, they're all the same pattern. |
The executor needs to be callable, so there shouldn't be a compatibility problem if we add an overload. |
JavaScript does not support overloading. |
Sure it does: function overloaded(arg) {
if (typeof arg === 'function') {
// do something
} else if (typeof arg === 'object' && arg !== null) {
// do something else
} else {
throw new TypeError;
}
} |
@bakkot yes, you can simulate it by type checking like that. I knew someone was going to mention that, I should have preempted it 😆 |
I don't want the result be |
That's not going to happen. The language is not going to provide a helper which attaches the ability to resolve a Promise to the Promise itself. |
If we go with overloading |
Why it have to be attached, it can be a method on prototype, only if the promise is constructed in this way, it takes effect. (I know there will be "confusion" between |
What about |
Words like |
Thanks all for explaining! Here's another non-method idea: add properties to the promise itself: var promise = new Promise(/* ... */)
promise.then(/* */)
promise.resolve() Is there something this would clash with? |
@mbrevda Putting properties on the Promise itself means that the ability to read the Promise is conflated with the ability to write to the Promise, which is a very bad idea. |
The referenced article makes an argument against the current design, too:
|
Although based on the above distinction (of internet vs external controll), perhaps some other method name ideas would include: Promise.external()
// or
Promise.controlled()
// or
Promise.managed()
// or
Promise.extract()
// or
Promise.extrinsic() |
@jnvm's suggestion looks very natural to me: const { resolve, reject, promise } = Promise.create(); It's just another way to create a Some thoughts:
Whatever the choice is, I think it's a good idea for the verb to indicate that something is actually being "created" - in the same way that it's clear that I guess another possibility is to be even more explicit with something like |
To be very clear (and to also clarify something I mis-said during this past TC39 meeting), resolvers is the correct terminology. The See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise .
I'm glad you bring up |
indeed, they are both called "resolving functions" in the spec
|
That implies to me the same thing as "unwrap", that it takes a promise and synchronously produces the result. |
Curious how viable resolve and reject are as instance methods on Promise. |
@lilnasy Not. See a few comments above. |
I don't think I've seen anyone suggest const { promise, resolve, reject } = Promise(); It seems to meet all of our criteria without having to choose a method name. |
...Except that overloading the call and construct signatures is probably a bad design post-ES6? |
constructor() {
if (new.target !== undefined) {
throw new TypeError;
}
// ...
} |
perhaps.. my initial thought is that it induces the is it better in some way than a named method on I think I'm starting to like it, but also seems like it would introduce a (minor) footgun, so maybe not |
What's the footgun? That somebody accidentally uses |
JS is weird enough as it is around including Also, I also agree with an earlier comment that |
I wouldn't mind if Promise.withResolvers moves forward as is. It's explicit, and it tells you about its relationship with Promise by being a static method on it. If that isn't a constraint, however, I'd like to propose a new class. const resolvable = new Resolvable()
{
...
resolvable.fulfill(value)
...
resolvable.reject(reason)
...
}
return resolvable.promise |
Resolvable implies to me that other promises aren’t resolvable. |
Getting back into the conversation here... I get that I think that a better return value, to better fit the name, would be: const [promise, { resolve, reject }] = Promise.withResolvers(); Further, at least to account for the other implication of the name, I think it maybe should [optionally] accept arguments for resolve and reject, defined externally to the promise: function resolve() {
/*...*/
}
function reject() {
/*...*/
}
const [promise] = Promise.withResolvers(resolve, reject);
resolve('foo'); The former makes more sense to me with what the intended meaning is, and the latter is more along the lines of what I'd initially expect of it. I think that returning I also think that being able to pass in |
Wouldn't that be an alternate execution / subscription model and be out of scope for naming this helper utility? external resolver thoughtsThis inverts the promise in a way that unhelpfully hides how the promise mananges its state and invokes subscribers. I was thinking it would break (like couldn't work) but maybe I'm just not creative enough to figure out how to subscribe to a callback function being invoked. It's cleaner to return the resolver so the promise can hook into its invocations while managing its state rather than accept a callback and listen to this external callback's invocation. Alsl what if resolve is invoked immediately? Do JS functions remember they are involved and would the promise instance now need to check that? Just seems far simpler to tie all resolvers existences to the promise instance directly and provide helpers to hook in after that, not before.
Could be off my rocker, happy to spar more, just seems like the kind of thing that 1) is not on point for this discussion and 2) would be dismissed almost off-hand either as an incompatible breaking change or as incurring additional complexity for implementors. |
Thanks to everyone for participating in this discussion. Although I have never considered To summarize my own rationale: Thanks again for everyone's participation! |
I am not agree with a name #17. |
Sorry I'm late to the party. If there's still a chance, consider "andResolvers" because to me, "withX" sounds like it adds an optional feature X to the Promise. |
That ship kinda sailed https://groups.google.com/a/chromium.org/g/blink-dev/c/JL4uXtfrCdU/m/aK1t8LJMAAAJ |
I am sure I'm not the only one who thinks
Promise.withResolvers()
does not sound right. To see the name, I would expect it to be something that you pass one or moreresolve
s into and it returns aPromise
.I think
getResolvers()
makes more sense, but it's still not descriptive of what it does. It returns thepromise
as well.I don't have a good name, but "extract", "expose", "unwrap", & "destructure" seem like more descriptive words that imply what it would do. What exactly would you call
{ promise, resolve, reject }
? What would you call the object that it returns?The text was updated successfully, but these errors were encountered: