> We already have a construct that automatically returns a promise, which resolves on return and rejects on throw. It's called an async function!
That's true, but marking a function as async doesn't (on its own) make it a substitute for something like setTimeout. On Mon, Dec 2, 2019 at 4:49 AM Frederick Stark <[email protected]> wrote: > We already have a construct that automatically returns a promise, which > resolves on return and rejects on throw. It's called an async function! > > > On Nov 30 2019, at 7:05 am, Lars Eidnes <[email protected]> wrote: > > Currently when awaiting calls there's some surprising behavior when > combining throw/catch and reject/resolve. > > ```js > function resolveAndThrow() { > return new Promise((resolve, reject) => { > resolve("huh"); > throw new Error(""); //<-- this throw is executed > }); > } > async function callAsync() { > try { > await resolveAndThrow(); > } catch (e) { > console.log("caught error", e); //<-- yet, this is error > logging never happens > } > } > ``` > > Here the error logging doesn't happen, because resolve() is called before > the throw, which causes the thrown error to be swallowed. Specifically, the > throw executes, and control flow is broken, but control flow never moves to > the catch {} block. This is a sort of dark corner, where language > mechanisms interact in conflicting ways. Most programmers wont have at > top-of-mind which of resolve and throw will "win" in a situation like the > one above. In fact, I suspect most JavaScript programmers haven't thought > about this issue at all until they encounter it. > > I've written a longer post about this error handling issue in JavaScript > some time ago: https://catchjs.com/Docs/AsyncAwait . There I mention how > C# has async/await, but does not have reject/resolve, and thus doesn't > really have this issue. There, exceptions thrown in async operations get > transferred to the whomever is awaiting the Task, and whatever is returned > from the Task is returned to whomever is awaiting it. So throw serves the > purpose of reject(), and return serves the purpose of resolve(). > > This is nice. It makes the reject/resolve mechanisms redundant, and > removes the paradoxical resolve+throw case shown above. Really, this > possible because the main interface for creating something awaitable (e.g. > Task.Run(() => {}) ) is more similar to setTimeout than to new Promise(). > Is it possible to introduce similar mechanisms to JavaScript? > > For reference, C# Tasks can be used like this: > > ``` > var result = await Task.Run(() => { Thread.Sleep(2); return 1; }); > //result now holds 1 > > try { > var result2 = await Task.Run(() => { > throw new Exception(""); //This error is tracked across > execution boundaries > return 1; > }); > } catch (Exception e) { > Console.WriteLine(e); //This error logging happens, with a full > stack trace from the await to the throw. > } > ``` > > The lack of resolve/reject has the benefit that there is no chance of > messing up with mixing throw and resolve. A key thing here is that thrown > exceptions are transported across execution contexts to wherever the Task > is awaited. > > The reason we have reject/resolve, as far as I can see, is to be able to > transport values and errors across execution context, for example so that > one can do this: > > ```js > function resolveInSetTimeout() { > return new Promise((resolve, reject) => { > setTimeout(() => resolve(1), 100); > }); > } > async function callAsync() { > var result = await resolveInSetTimeout(); //result now holds 1 > } > ``` > > In JavaScript, the place where an API similar to Task could be useful > would be as a replacement for setTimeout(callback). Some new API that takes > a callback, returns a Promise, and where thrown errors in the callback are > transported back to anyone awaiting the Promise, and where returned values > in the callback resolve the Promise. It seems to me that transporting the > thrown error across execution boundaries requires language/runtime support, > at least if we want to get proper stack traces. > > If libraries with async APIs were built on this mechanism, we could use > them and ensure that we can do error tracking for any errors happening > inside setTimeouts. Now we're at the mercy of the library author > remembering to catch the error and passing it to reject(). > > I've written a lot here, and I suspect this mailing list is better > equipped than I am to figure out what's a good idea in this context. Two > questions to summarize: > > 1) Is it a good idea to introduce an alternative to setTimeout, where the > distinction is that it returns a Promise, and that return/throw in the > callback take the role of resolve/reject? > > 2) Are there other things we could do to minimize the need for > resolve/reject? > > What do you think? > > _______________________________________________ > es-discuss mailing list > [email protected] > https://mail.mozilla.org/listinfo/es-discuss > >
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

