{"action":"created","issue":{"url":"https://api.github.com/repos/lukehoban/ecmascript-asyncawait/issues/38","labels_url":"https://api.github.com/repos/lukehoban/ecmascript-asyncawait/issues/38/labels{/name}","comments_url":"https://api.github.com/repos/lukehoban/ecmascript-asyncawait/issues/38/comments","events_url":"https://api.github.com/repos/lukehoban/ecmascript-asyncawait/issues/38/events","html_url":"https://github.com/lukehoban/ecmascript-asyncawait/issues/38","id":66490467,"number":38,"title":"Grammar for interfacing existing callback code with async/await","user":{"login":"MatAtBread","id":2789075,"avatar_url":"https://avatars.githubusercontent.com/u/2789075?v=3","gravatar_id":"","url":"https://api.github.com/users/MatAtBread","html_url":"https://github.com/MatAtBread","followers_url":"https://api.github.com/users/MatAtBread/followers","following_url":"https://api.github.com/users/MatAtBread/following{/other_user}","gists_url":"https://api.github.com/users/MatAtBread/gists{/gist_id}","starred_url":"https://api.github.com/users/MatAtBread/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MatAtBread/subscriptions","organizations_url":"https://api.github.com/users/MatAtBread/orgs","repos_url":"https://api.github.com/users/MatAtBread/repos","events_url":"https://api.github.com/users/MatAtBread/events{/privacy}","received_events_url":"https://api.github.com/users/MatAtBread/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":6,"created_at":"2015-04-05T22:34:52Z","updated_at":"2015-04-06T02:51:33Z","closed_at":null,"body":"Whilst Promises provide an mechanism to relate the (considerable) body of callback-based code with async functions, the syntax is relatively verbose and implementation dependent (i.e. it requires and relies on knowledge of the spawn() function) potentially restricting the possible set of implementations.\r\n\r\nThis issue suggests a mechanism to minimize the boilerplate needed to interface existing code with async/await by allowing callbacks contained within async functions to resolve and reject their container async functions, and prevent the container resolving automatically on exit without a return or throw.\r\n\r\nConsider the code below, which has only 1 functional line (2 including the declaration):\r\n````\r\nfunction sleep(t) {\r\n    return new Promise(function(resolve,reject) {\r\n        setTimeout(resolve,t) ;\r\n    }) ;\r\n}\r\n````\r\nAn alternative would be to pass the resolve/reject parameters to the re-written generator with two additional parameters (in this example asyncReturn and asyncThrow), parsing\r\n````\r\nasync function sleep(t) {\r\n    setTimeout(asyncReturn,t) ;\r\n}\r\n````\r\nto\r\n````\r\nfunction sleep(t) {\r\n    return spawn(function*(asyncReturn,asyncError){\r\n        setTimeout(asyncReturn,t) ;\r\n    }) ;\r\n}\r\n````\r\nThe same translation works for direct calls as well:\r\n````\r\nasync function get(url) {\r\n    var x = new XMLHttpRequest() ;\r\n    x.onload = function(){\r\n        if (x.status===200)\r\n            return asyncReturn(x.responseText) ;\r\n        else\r\n            return asyncError(new Error(x.responseText)) ;\r\n    } ;\r\n   x.open(url) ;\r\n   x.send() ;\r\n}\r\n````\r\n...translates to\r\n````\r\nfunction get(url) {\r\n    return spawn(function*(async,asyncError){\r\n      var x = new XMLHttpRequest() ;\r\n      x.onload = function(){\r\n          if (x.status===200)\r\n              return asyncReturn(x.responseText) ;\r\n          else\r\n              return asyncError(new Error(x.responseText)) ;\r\n      } ;\r\n     x.open(url) ;\r\n     x.send() ;\r\n   })\r\n}\r\n````\r\nThis can be easily implemented by:\r\n1) Passing the Promise resolve and reject routines in spawn():\r\n````\r\nfunction spawn(genF,self) {\r\n    return new Promise(function(resolve, reject) {\r\n        var gen = genF.call(self,resolve,reject);\r\n        ....\r\n````\r\n2) Allowing async functions to NOT automatically resolve on return, but providing a method to indicate resolution will take place in the future. I have implemented this by disallowing the resolved value to be the `resolver` argument (since this is pointless), and always appending a synchronous return to the wrapped generator body.\r\n\r\nThe rewrite rule now becomes:\r\n````\r\nasync function <name>?<argumentlist>{<body>}\r\n=>\r\nfunction <name>?<argumentlist>{ return spawn(\r\n    function*(asyncReturn,asyncError) {<body> ; return asyncReturn ; },\r\nthis); }\r\n````\r\n...and spawn is updated to:\r\n````\r\nfunction spawn(genF,self) {\r\n    return new Promise(function(resolve, reject) {\r\n        var gen = genF.call(self,resolve,reject);\r\n        function step(nextF) {\r\n            var next;\r\n            try {\r\n                next = nextF();\r\n            } catch(e) {\r\n                // finished with failure, reject the promise\r\n                reject(e); \r\n                return;\r\n            }\r\n            if(next.done) {\r\n                // finished with success, resolve the promise\r\n                // ...unless the response indicates resolution by callback\r\n                if (next.value!==resolve)\r\n                    resolve(next.value);\r\n                return;\r\n            } \r\n            // not finished, chain off the yielded promise and `step` again\r\n            Promise.cast(next.value).then(function(v) {\r\n                step(function() { return gen.next(v); });      \r\n            }, function(e) {\r\n                step(function() { return gen.throw(e); });\r\n            });\r\n        }\r\n        step(function() { return gen.next(undefined); });\r\n    });\r\n}\r\n````\r\nSyntax:\r\nThe use of the identifier `asyncReturn` (and `asyncError`) is possibly not wise. Additional rewrites would be required, but preferential syntaxes might be:\r\n````\r\nreturn async <expr>     =>     return <arg1>(<expr>)\r\nthrow async <expr>      =>     return <arg2>(<expr>)\r\n````\r\n"},"comment":{"url":"https://api.github.com/repos/lukehoban/ecmascript-asyncawait/issues/comments/89897178","html_url":"https://github.com/lukehoban/ecmascript-asyncawait/issues/38#issuecomment-89897178","issue_url":"https://api.github.com/repos/lukehoban/ecmascript-asyncawait/issues/38","id":89897178,"user":{"login":"MatAtBread","id":2789075,"avatar_url":"https://avatars.githubusercontent.com/u/2789075?v=3","gravatar_id":"","url":"https://api.github.com/users/MatAtBread","html_url":"https://github.com/MatAtBread","followers_url":"https://api.github.com/users/MatAtBread/followers","following_url":"https://api.github.com/users/MatAtBread/following{/other_user}","gists_url":"https://api.github.com/users/MatAtBread/gists{/gist_id}","starred_url":"https://api.github.com/users/MatAtBread/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MatAtBread/subscriptions","organizations_url":"https://api.github.com/users/MatAtBread/orgs","repos_url":"https://api.github.com/users/MatAtBread/repos","events_url":"https://api.github.com/users/MatAtBread/events{/privacy}","received_events_url":"https://api.github.com/users/MatAtBread/received_events","type":"User","site_admin":false},"created_at":"2015-04-06T02:51:33Z","updated_at":"2015-04-06T02:51:33Z","body":"Dominic - you're a very clever guy, and I'm a big fan of your blog and work, but I don't see how that moves things forward. For us mortals, language support for otherwise fiddly constructs (like making async/await different from yield/generators - you made a very good comment on why this is a good thing on https://esdiscuss.org/topic/does-async-await-solve-a-real-problem) is one way to help us. Indeed in that post, you made a convincing case for using the language to do something that could easily be done by a search and replace (although I think it's also a good move for VM providers who can optimize that case separately from the iteration case).\r\n\r\nYou may wish there was no need for this use case, but unfortunately there is. Since we're in the early stages of working out exactly what opportunities and difficulties async and await introduce, maybe it's worth taking on board everyone's experiences and seeing which common cases would benefit from language support?"}}