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

Reply via email to