> The way promises are chained means that some patterns like async loops would 
> theoretically unroll to very long stack traces

Devtools in fact does that (has a button at the bottom of the stack to
keep loading more frames).

I think it would be better for it to be smart and not have duplicate
frames in the stack, so a loop would result in only one unique set of
entries in the stack trace and not repeat.

#!/JoePea

On Sun, Jul 12, 2020 at 11:53 AM Jacob Bloom <[email protected]> wrote:
>
> Reading through the issue JoePea linked to, it looks like the difficulties 
> with standardized async stack traces are twofold:
>
> 1. Even with the error stacks proposal, the implementer has total say over 
> what qualifies as a stack frame
> 2. The way promises are chained means that some patterns like async loops 
> would theoretically unroll to very long stack traces, and right now engines 
> aren't required to keep track of those frames; requiring them to do so would 
> place a burden on implementers that could quickly lead to slowdowns and 
> memory issues
>
> What if there was a standard way to mark a Promise as an important stack 
> frame, which the implementer is free to ignore? Maybe something like 
> `Promise.prototype.trace()`
>
> On Fri, Jul 10, 2020 at 10:22 PM #!/JoePea <[email protected]> wrote:
>>
>> Hello Kai! That example is so meta with its own output showing the
>> numbers relative to the code including its own output! :)
>>
>> That's what I originally wanted to do, but that doesn't give us an
>> async stack trace, it only gives a sync stack trace (I presume within
>> the current event loop task).
>>
>> For example:
>>
>> ```js
>> const originalFetch = globalThis.fetch
>>
>> globalThis.fetch = function(...args) {
>>   const stack = new Error().stack
>>   console.log(stack)
>>   return originalFetch.apply(globalThis, args)
>> }
>>
>> const sleep = t => new Promise(r => setTimeout(r, t))
>>
>> async function one() {
>>   console.log('1')
>>   await sleep(10)
>>   two()
>> }
>>
>> async function two() {
>>   console.log('2')
>>   await sleep(10)
>>   three()
>> }
>>
>> async function three() {
>>   console.log('3')
>>   await sleep(10)
>>   return await fetch('https://unpkg.com/[email protected]')
>> }
>>
>> async function main() {
>>   await one()
>> }
>>
>> main()
>> ```
>>
>> Output:
>>
>> ```
>> 1
>> 2
>> 3
>> Error
>>     at globalThis.fetch (pen.js:5)
>>     at three (pen.js:27)
>> ```
>>
>> Live example:
>>
>> https://codepen.io/trusktr/pen/b8fd92752b4671268f516ad3804869e4?editors=1010
>>
>> I opened a request for this over here:
>> https://github.com/tc39/proposal-error-stacks/issues/34
>>
>> #!/JoePea
>>
>> On Fri, Jul 10, 2020 at 1:21 PM kai zhu <[email protected]> wrote:
>> >
>> > >  (I want to detect traces in third-party code installed locally).
>> >
>> > 1. here's 15-line javascript-hack to trace async-fetch-calls from 
>> > 3rd-party-cdn-library
>> >
>> > ```html
>> > <!doctype html>
>> > <html lang="en">
>> > <body>
>> > <h1>test.html</h1>
>> > <script>
>> > (function () {
>> > /*
>> >  * 15-line-javascript-hack to trace async-fetch-calls
>> >  * enable hack by adding search-query "?modeDebugFetch=1" to web-url
>> >  */
>> >     "use strict";
>> >     if ((/\bmodeDebugFetch=1\b/).test(location.search)) {
>> >         let fetch0;
>> >         fetch0 = globalThis.fetch;
>> >         globalThis.fetch = function (...argList) {
>> >             let errStack = new Error().stack;
>> >             console.error("\n\nfetch-call-arguments:");
>> >             console.error(JSON.stringify(argList, undefined, 4));
>> >             console.error("\n\nfetch-call-trace:");
>> >             console.error(errStack);
>> >             return fetch0(...argList);
>> >         };
>> >     }
>> > }());
>> > </script>
>> > <!-- 3rd-party-cdn-library -->
>> > <script src="https://unpkg.com/http-client/umd/http-client.js";></script>
>> > <script>
>> > (async function foo() {
>> >     "use strict";
>> >     let thirdParty = window.HTTPClient;
>> >     let myFetch = thirdParty.createFetch(
>> >         thirdParty.base("https://api.stripe.com/v1";),
>> >         thirdParty.accept("application/json")
>> >     );
>> >     let response = await myFetch("/customers/5");
>> >     console.log(response.jsonData);
>> > /*
>> > dev-console-output - http://localhost:8081/test.html?modeDebugFetch=1
>> > fetch-call-arguments:
>> > [
>> >     "https://api.stripe.com/v1/customers/5";,
>> >     {
>> >         "headers": {
>> >             "Accept": "application/json"
>> >         },
>> >         "responseHandlers": [
>> >             null
>> >         ]
>> >     }
>> > ]
>> > fetch-call-trace:
>> > Error
>> >     at globalThis.fetch (test.html?modeDebugFetch=1:16)
>> >     at http-client.js:194
>> >     at http-client.js:127
>> >     at http-client.js:217
>> >     at http-client.js:126
>> >     at http-client.js:154
>> >     at http-client.js:95
>> >     at foo (test.html?modeDebugFetch=1:35)
>> >     at test.html?modeDebugFetch=1:64
>> > */
>> > }());
>> > </script>
>> > </body>
>> > ```
>> >
>> >
>> > 2. here's real-world-hack added to npm-cli.js to debug its http-requests
>> >
>> > ```diff
>> > C:\Program Files\nodejs\node_modules\npm>git diff
>> > diff --git a/bin/npm-cli.js b/bin/npm-cli.js
>> > index 561dec0..98cafb8 100644
>> > --- a/bin/npm-cli.js
>> > +++ b/bin/npm-cli.js
>> > @@ -2,17 +2,6 @@
>> >  ;(function () { // wrapper in case we're in module_context mode
>> > +// hack - debug http-request
>> > +let httpRequest;
>> > +httpRequest = require("https").request.bind(require("https"));
>> > +require("https").request = function (...argList) {
>> > +    if (process.env.NPM_DEBUG) {
>> > +        console.error(
>> > +            "npm - httpRequest - " + JSON.stringify(argList.slice(0, 2), 
>> > undefined, 4)
>> > +        );
>> > +    }
>> > +    return httpRequest(...argList);
>> > +};
>> >    // windows: running "npm blah" in this folder will invoke WSH, not node.
>> >
>> > C:\Program Files\nodejs\node_modules\npm>
>> > ```
>> >
>> > ```mingw-bash
>> > $ NPM_DEBUG=1 npm publish foo
>> > # console-ouput:
>> > # npm - httpRequest - [
>> > #     {
>> > #         "protocol": "https:",
>> > #         "href": "https://registry.npmjs.org/foo";,
>> > #         "method": "GET",
>> > #         "headers": {
>> > #             "connection": [
>> > #                 "keep-alive"
>> > #             ],
>> > #             "user-agent": [
>> > #                 "npm/6.13.4 node/v12.16.1 win32 x64"
>> > #             ],
>> > #             ...
>> > ```
>> >
>> >
>> >
>> >
>> >
>> > On Fri, Jul 10, 2020 at 3:34 AM #!/JoePea <[email protected]> wrote:
>> >>
>> >> Thanks for the idea! That's similar to what I thought would be necessary. 
>> >> I was hoping to monkey patch `fetch()` to have it get the trace if any 
>> >> `fetch` call, but they would not be async stack traces. For async traces 
>> >> it would require instrumentation by injecting code similar to yours (I 
>> >> want to detect traces in third-party code installed locally).
>> >>
>> >> #!/JoePea
>> >>
>> >> On Wed, Jul 8, 2020, 2:13 AM kai zhu <[email protected]> wrote:
>> >>>
>> >>> here's a simple throwaway-function you can wrap around promises like 
>> >>> fetch
>> >>> to get the caller's async-stack-trace `promiseWithErrorStack`:
>> >>>
>> >>> ```html
>> >>> <!doctype html>
>> >>> <html lang="en">
>> >>> <body>
>> >>> <h1>test.html</h1>
>> >>> <script>
>> >>> (async function foo() {
>> >>>     function promiseWithErrorStack(promise) {
>> >>>     /*
>> >>>      * this function will append current-stack to any err caught from 
>> >>> <promise>
>> >>>      */
>> >>>         let errStack;
>> >>>         errStack = new Error().stack;
>> >>>         return new Promise(function (resolve, reject) {
>> >>>             promise.then(resolve).catch(function (err) {
>> >>>                 // append current errStack to err.stack
>> >>>                 if (err && typeof err.stack === "string") {
>> >>>                     err.stack += "\n" + errStack;
>> >>>                 }
>> >>>                 reject(err);
>> >>>             });
>> >>>         });
>> >>>     }
>> >>>     await promiseWithErrorStack(fetch("https://example.com";)); // at foo 
>> >>> (test.html:23)
>> >>>
>> >>>     /*
>> >>>     console-output:
>> >>>
>> >>>     Uncaught (in promise) TypeError: Failed to fetch
>> >>>     Error
>> >>>         at promiseWithErrorStack (test.html:12)
>> >>>         at foo (test.html:23) // async-stack-trace
>> >>>         at test.html:32
>> >>>     */
>> >>> }());
>> >>> </script>
>> >>> </body>
>> >>> ```
>> >>>
>> >>> On Tue, Jul 7, 2020 at 11:54 PM #!/JoePea <[email protected]> wrote:
>> >>>>
>> >>>> Is there some way (perhaps by patching methods on objects?) so we can
>> >>>> track async call stacks?
>> >>>>
>> >>>> When we pause code in devtools, we are able to see the async stack
>> >>>> trace of the code.
>> >>>>
>> >>>> What I'd like to do is to effectively detect the same thing as
>> >>>> devtools does at some point in any code. As a specific example, I'd
>> >>>> like to detect the async stack trace of any call to `fetch`.
>> >>>>
>> >>>> Is such a thing possible with runtime code?
>> >>>>
>> >>>> Or would it require instrumentation of the source code?
>> >>>>
>> >>>> #!/JoePea
>> >>>> _______________________________________________
>> >>>> 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
>
> _______________________________________________
> 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

Reply via email to