{"action":"closed","issue":{"url":"https://api.github.com/repos/nodejs/node/issues/11865","repository_url":"https://api.github.com/repos/nodejs/node","labels_url":"https://api.github.com/repos/nodejs/node/issues/11865/labels{/name}","comments_url":"https://api.github.com/repos/nodejs/node/issues/11865/comments","events_url":"https://api.github.com/repos/nodejs/node/issues/11865/events","html_url":"https://github.com/nodejs/node/issues/11865","id":214422253,"number":11865,"title":"Missing stack traces from async functions after the first await","user":{"login":"TazmanianDI","id":25828402,"avatar_url":"https://avatars1.githubusercontent.com/u/25828402?v=4","gravatar_id":"","url":"https://api.github.com/users/TazmanianDI","html_url":"https://github.com/TazmanianDI","followers_url":"https://api.github.com/users/TazmanianDI/followers","following_url":"https://api.github.com/users/TazmanianDI/following{/other_user}","gists_url":"https://api.github.com/users/TazmanianDI/gists{/gist_id}","starred_url":"https://api.github.com/users/TazmanianDI/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TazmanianDI/subscriptions","organizations_url":"https://api.github.com/users/TazmanianDI/orgs","repos_url":"https://api.github.com/users/TazmanianDI/repos","events_url":"https://api.github.com/users/TazmanianDI/events{/privacy}","received_events_url":"https://api.github.com/users/TazmanianDI/received_events","type":"User","site_admin":false},"labels":[{"id":176191361,"url":"https://api.github.com/repos/nodejs/node/labels/V8%20Engine","name":"V8 Engine","color":"0052cc","default":false},{"id":207445406,"url":"https://api.github.com/repos/nodejs/node/labels/feature%20request","name":"feature request","color":"0D7BDE","default":false},{"id":340436277,"url":"https://api.github.com/repos/nodejs/node/labels/promises","name":"promises","color":"f7c6c7","default":false},{"id":151728679,"url":"https://api.github.com/repos/nodejs/node/labels/question","name":"question","color":"cc317c","default":true}],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":41,"created_at":"2017-03-15T15:13:13Z","updated_at":"2018-03-04T19:30:14Z","closed_at":"2018-03-04T19:30:14Z","author_association":"NONE","body":"Version: v7.7.3\r\nPlatform: Windows 7x64\r\n\r\nThe purpose of this issue is really a more broad request for better stack traces with async/await but I figured I would start with a very specific case. If we need to broaden the description, I'm good with that. I've seen a number of lengthy discussion on this subject in various places but I don't see any actual issues for it so I thought I'd start one and hopefully it's not just a duplicate of something I missed.\r\n\r\nI'm filing the specific issue here because it seems the async/await functionality just added provides less useful error handling than we could get with generators.\r\n\r\n```\r\nasync function functionOne() {\r\n  await new Promise((resolve) => {\r\n    setTimeout(() => { resolve(); }, 1);\r\n  });\r\n  throw new Error('Something Bad');\r\n}\r\n\r\nasync function functionTwo() {\r\n  await functionOne();\r\n}\r\n\r\nfunctionTwo()\r\n  .catch((error) => {\r\n    console.error(error);\r\n  });\r\n```\r\nOutputs:\r\n```\r\nError: Something Bad\r\n    at functionOne (C:\\Work\\sandbox.js:5:9)\r\n```\r\n\r\nThat stack is missing everything that called `functionOne` (`functionTwo` specifically).\r\n\r\nThe generator equivalent of this:\r\n\r\n```\r\nconst co = require('co');\r\n\r\nfunction* functionOne() {\r\n  yield new Promise((resolve) => {\r\n    setTimeout(() => { resolve(); }, 1);\r\n  });\r\n  throw new Error('Something Bad');\r\n}\r\n\r\nfunction* functionTwo() {\r\n  yield* functionOne();\r\n}\r\n\r\nco(functionTwo())\r\n  .catch((error) => {\r\n    console.log(error);\r\n  });\r\n```\r\nOutputs:\r\n```\r\nError: Something Bad\r\n    at functionOne (C:\\Work\\sandbox.js:7:9)\r\n    at functionOne.next (<anonymous>)\r\n    at functionTwo (C:\\Work\\sandbox.js:11:10)\r\n    at functionTwo.next (<anonymous>)\r\n    at onFulfilled (C:\\Work\\NPS\\nps-dev\\node_modules\\co\\index.js:65:19)\r\n```\r\n\r\nHere you can see both `functionOne` and `functionTwo` in the stack.\r\n\r\nIf the error is thrown before any `await` in the code, then you actually get a complete stack trace even if the function was called in a whole chain of awaits and regardless if those awaits were first or not:\r\n\r\n```\r\nasync function throwFunction() {\r\n  throw new Error('Something bad');\r\n}\r\n\r\nasync function functionOne() {\r\n  return await throwFunction();\r\n}\r\n\r\nasync function functionTwo() {\r\n  return await Promise.resolve();\r\n}\r\n\r\nasync function functionThree() {\r\n  await functionTwo();\r\n  return await functionOne();\r\n}\r\n\r\nfunctionThree()\r\n  .catch((error) => {\r\n    console.log(error);\r\n  });\r\n```\r\nOutputs:\r\n```\r\nError: Something bad\r\n    at throwFunction (C:\\Work\\sandbox.js:2:9)\r\n    at functionOne (C:\\Work\\sandbox.js:6:16)\r\n    at functionThree (C:\\Work\\sandbox.js:15:16)\r\n    at process._tickCallback (internal/process/next_tick.js:109:7)\r\n    at Module.runMain (module.js:607:11)\r\n    at run (bootstrap_node.js:425:7)\r\n    at startup (bootstrap_node.js:146:9)\r\n    at bootstrap_node.js:540:3\r\n```\r\n\r\nThe real driving force behind this was that I finally found the recipe to get complete stack traces, even when dealing with existing code using promises. With a try...catch in the generator, we can use `VError` to weld together the errors thrown by promises with the stack of the code calling the generator. This does not seem to work with async functions.\r\n\r\nHere's a more complete example using generators that I really wish would continue to work with async functions:\r\n\r\n```\r\nconst co = require('co');\r\nconst VError = require('verror');\r\n\r\nfunction calledFromAPromise() {\r\n  throw new Error('Something bad');\r\n}\r\n\r\nfunction doAPromise() {\r\n  return new Promise((resolve) => {\r\n    setTimeout(() => { resolve(); }, 1);\r\n  })\r\n    .then(() => { calledFromAPromise(); });\r\n}\r\n\r\nfunction* queryFunction() {\r\n  return yield* yieldRethrow(doAPromise());\r\n}\r\n\r\nfunction* functionOne() {\r\n  return yield* queryFunction();\r\n}\r\n\r\nfunction* functionTwo() {\r\n  return yield* functionOne();\r\n}\r\n\r\nfunction* yieldRethrow(iterable) {\r\n  try {\r\n    return yield iterable;\r\n  } catch (error) {\r\n    throw new VError(error);\r\n  }\r\n}\r\n\r\nco(functionTwo())\r\n  .catch((error) => {\r\n    console.log(error);\r\n  });\r\n```\r\nOutputs (with some non-relevant stuff removed):\r\n```\r\n{ VError: : Something bad\r\n    at yieldRethrow (C:\\Work\\sandbox.js:31:11)\r\n    at yieldRethrow.throw (<anonymous>)\r\n    at queryFunction (C:\\Work\\sandbox.js:16:17)\r\n    at functionOne (C:\\Work\\sandbox.js:20:17)\r\n    at functionTwo (C:\\Work\\sandbox.js:24:17)\r\n    at onRejected (C:\\Work\\NPS\\nps-dev\\node_modules\\co\\index.js:81:24)\r\n  jse_cause: \r\n   Error: Something bad\r\n       at calledFromAPromise (C:\\Work\\sandbox.js:5:9)\r\n       at Promise.then (C:\\Work\\sandbox.js:12:19),\r\n```\r\nThe async equivalent outputs this:\r\n```\r\n{ VError: : Something bad\r\n    at yieldRethrow (C:\\Work\\sandbox.js:30:11)\r\n  jse_cause: \r\n   Error: Something bad\r\n       at calledFromAPromise (C:\\Work\\sandbox.js:4:9)\r\n       at Promise.then (C:\\Work\\sandbox.js:11:19),\r\n```\r\n\r\nAs you can see, this has the same problem as at the top in that the rethrown error doesn't have a complete stack."}}
{"actor":{"display_login":"BridgeAR"}}