20:40
<bakkot>

I have been writing lots of promises lately and keep getting annoyed by awkwardness of extracting the handlers from the promise constructor. kind of want to add a helper for this. thoughts?

Promise.create = () => {
  let resolve, reject;
  let promise = new Promise((res, rej) => {
    resolve = res;
    reject = rej;
  });
  return { resolve, reject, promise };
};

let { resolve, reject, promise } = Promise.create();
resolve(42);
promise; // Promise {<fulfilled>: 42}
20:41
<bakkot>
I could just copy that helper to every project but it comes up often enough for me that it seems maybe worth adding. don't know if this is something other people run into though.
20:43
<nicolo-ribaudo>
There is some popular prior art: jQuery's `$.deferred()` exposes the reject/resolve methods, and a `.promise()` method to get the internal promise.
21:32
<joepie91 🏳️‍🌈>
bakkot: the problem with deferreds like that is that, while there is a nonzero amount of cases where it's the correct solution (particularly queues), when people use it it is almost always because they are using promises wrong
21:33
<joepie91 🏳️‍🌈>
and aren't following the principle of "your new Promise should only contain the conversion logic necessary for a specific obsolete asynchronous API, nothing more, nothing less"
21:33
<joepie91 🏳️‍🌈>
and it often leads to race conditions and other reliability issues
21:33
<joepie91 🏳️‍🌈>
so I'd say that if you find yourself needing them in 'every project', that's probably a red flag, and it's also not something that should be too easy to do precisely because of its misuse potential :)
21:34
<joepie91 🏳️‍🌈>
(afaik this is basically why most modern Promise implementations ended up not implementing a deferred API)
22:07
<bakkot>
I certainly can believe that many people would use it wrong, and that's maybe a reason not to do this, though I don't agree that you should always be trying to shove your conversion logic inside of the call to the promise constructor
22:07
<bakkot>
also I guess I do write a lot of queues, which is probably unusual
22:54
<joepie91 🏳️‍🌈>
I certainly can believe that many people would use it wrong, and that's maybe a reason not to do this, though I don't agree that you should always be trying to shove your conversion logic inside of the call to the promise constructor
the primary reason for doing so, aside from the self-contained nature of the resulting promise, is that it will also capture synchronously thrown errors in the conversion code and propagate them as promise rejections
22:54
<joepie91 🏳️‍🌈>
otherwise you run the risk of ending up with a half-async-half-sync API
22:55
<joepie91 🏳️‍🌈>
(this doesn't apply for queues where this is generally not reasonably possible at all; I'm just talking about conversions from weird async-cb APIs to promises here)
23:15
<bakkot>
I don't usually want to capture synchronously thrown errors in the conversion code, personally, because that means that I messed up registering the callback rather than that the thing I was registering it to threw
23:16
<bakkot>
like if I'm doing thing.on('finished', resolve(val)), but thing is accidentally null at that point, that's a synchronous error and I don't want it to be wrapped up in a promise
23:16
<bakkot>
this is what I mean about it not making sense to try to shove all of your conversion logic inside the call to the promise constructor
23:17
<bakkot>
I only want an error if thing itself produced an error, rather than if my conversion code failed