<div dir="ltr"><div dir="ltr"><div dir="ltr">> We already have a construct that automatically returns a promise, which resolves on return and rejects on throw. It's called an <code>async</code>  function!</div><div dir="ltr"><div dir="ltr"><br></div><div dir="ltr">That's true, but marking a function as async doesn't (on its own) make it a substitute for something like setTimeout. </div></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Dec 2, 2019 at 4:49 AM Frederick Stark <<a href="mailto:coagmano@gmail.com">coagmano@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>We already have a construct that automatically returns a promise, which resolves on return and rejects on throw. It's called an <code>async</code>  function!</div><br><br><div><u></u><u></u></div><div>On Nov 30 2019, at 7:05 am, Lars Eidnes <<a href="mailto:larseidnes@gmail.com" target="_blank">larseidnes@gmail.com</a>> wrote:</div><blockquote><div><div><div><div>Currently when awaiting calls there's some surprising behavior when combining throw/catch and reject/resolve.</div><div><br></div><div>```js</div><div>    function resolveAndThrow() {</div><div>        return new Promise((resolve, reject) => {</div><div>            resolve("huh");</div><div>            throw new Error(""); //<-- this throw is executed</div><div>        });</div><div>    }</div><div>    async function callAsync() {</div><div>        try {</div><div>            await resolveAndThrow();</div><div>        } catch (e) {</div><div>            console.log("caught error", e); //<-- yet, this is error logging never happens</div><div>        }</div><div>    }</div><div>```</div><div><br></div><div>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.</div><div><br></div><div>I've written a longer post about this error handling issue in JavaScript some time ago: <a href="https://catchjs.com/Docs/AsyncAwait" title="https://catchjs.com/Docs/AsyncAwait" target="_blank">https://catchjs.com/Docs/AsyncAwait</a> . 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(). </div><div><br></div><div>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? </div><div><br></div><div>For reference, C# Tasks can be used like this:</div><div><br></div><div>```</div><div>    var result = await Task.Run(() => { Thread.Sleep(2); return 1; });</div><div>    //result now holds 1</div><div>    </div><div>    try {</div><div>        var result2 = await Task.Run(() => { </div><div>            throw new Exception(""); //This error is tracked across execution boundaries</div><div>            return 1;</div><div>        }); </div><div>    } catch (Exception e) { </div><div>        Console.WriteLine(e); //This error logging happens, with a full stack trace from the await to the throw.</div><div>    }</div><div>```</div><div><br></div><div>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. </div><div><br></div><div>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:</div><div><br></div><div>```js</div><div>    function resolveInSetTimeout() {</div><div>        return new Promise((resolve, reject) => {</div><div>            setTimeout(() => resolve(1), 100);</div><div>        });</div><div>    }</div><div>    async function callAsync() {</div><div>        var result = await resolveInSetTimeout(); //result now holds 1</div><div>    }</div><div>```</div><div><br></div><div>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.</div><div><br></div><div>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().</div><div><br></div><div>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:</div><div><br></div><div>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?</div><div><br></div><div>2) Are there other things we could do to minimize the need for resolve/reject? </div><div><br></div><div>What do you think?</div><div><br></div></div></div><div>_______________________________________________</div><div>es-discuss mailing list</div><div><a href="mailto:es-discuss@mozilla.org" target="_blank">es-discuss@mozilla.org</a></div><div><a href="https://mail.mozilla.org/listinfo/es-discuss" target="_blank">https://mail.mozilla.org/listinfo/es-discuss</a></div></div></blockquote></blockquote></div>