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 <larseid...@gmail.com> 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 > es-discuss@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss >
_______________________________________________ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss