10 ms·
Async/Await will make code simpler
- mpweiher 9y agoI go back and forth on async/await. On the one hand, it is utterly brilliant. On the other hand, it seems like the final epicycle, trying to fit a theory of circular geocentric orbits (call/return) onto a real world of elliptical heliocentric ones (asynchronous programming). So yes, it will make code easier, but I fear that will only serve to prolong the dominance of what is arguably the wrong programming model/architectural style. Or more precisely: an insufficient programming model/architectural style (it is great for a lot of things, just not for all).
- throwasehasdwi 9y agoawait/promises/etc mostly exist to solve the problem that JavaScript doesn't have threads so it can't wait for callbacks. About JavaScript, many other languages have had async/await for a long time. I have no idea why JS made such a huge deal of promises, I guess they're better than the callback hell before. Of course, in most languages using async isn't nearly as important for performance because they have thread pools. Some interfaces aren't and won't be asynchronous (like Linux file IO) so eventually JS will support proper threads and we can stop talking about how great asynchronous programming is (it isn't).
- jrs95 9y agoNode has threads in C++ for that sort of thing. Async programming is a big deal for performance. That's essentially the main reason Node is any faster than Python. If you use fully async Python on uvloop, you can get comparable performance.
- balfirevic 9y agoIt is ultimately a failure of language and runtime that programmer has to manually specify where he wants to make asynchronous vs. synchronous functions to get the optimal performance. This blog posts elaborates on that better then I could do here, so I'm just going to link to it: http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...
- jcelerier 9y ago> that programmer has to manually specify where he wants to make asynchronous vs. synchronous functions to get the optimal performance. programs aren't just pure computations. There are plenty of times when you want a specific event to happen at a specific time (as in, wall-clock), and plenty of times when you don't care when something computes as long as you end up getting a result at some point.
- throwasehasdwi 9y agoYou don't get reliable wall clock time unless you're working in RTOS. In a threaded OS everything in userland is async to an extent. In this school of thought, having to specify that something should be async manually could be seen as a failure of the language.
- jcelerier 9y agoon recent good hardware there is no problem being around 1ms accuracy.
- balfirevic 9y agoYes, but that should be call-time distinction, not a function declaration distinction. This is briefly mentioned at the end of the linked article. Edit: as other poster mentioned, async/await and promises also don't help with precise wall-clock time but that is entirely different matter.
- coldtea 9y ago>That's essentially the main reason Node is any faster than Python. v8's JIT is many times faster than Python in CPU bound tasks without any asynchronicity involved.
- flamedoge 9y agoPython also has async/await
- jdmichal 9y agoMaybe promises come before async/await, in an evolutionary sense. Same way that for-each loops seem to precede generators / yield. C# added Task before the syntax sugar of async/await. Java currently has Future, and I expect async/await to finally show up in about 10 years time...
- iainmerrick 9y agoawait/promises/etc mostly exist to solve the problem that JavaScript doesn't have threads async/await was popularized in C#, which does have threads.
- Rusky 9y agoThe way I look at it is as just another way of manipulating continuations, alongside conditional, loops, call/return, generators, coroutines, exceptions, etc. Even in some other hypothetical asynchronous paradigm, all those patterns still occur all over the place, so making them all composable with each other is a win. (Credit to http://blog.paralleluniverse.co/2015/08/07/scoped-continuations/ http://blog.paralleluniverse.co/2015/08/07/scoped-continuati... for describing it this way.) The surface syntax could maybe use some work- maybe make functions generic across asynchronicity, maybe swap `await` to being the default and explicitly mark the case where you want a reified Promise instead, etc. But in the end it's another valuable control structure.
- coldtea 9y ago>So yes, it will make code easier, but I fear that will only serve to prolong the dominance of what is arguably the wrong programming model/architectural style. Nested callbacks and even Promises are not the "right model/style" by any measure. Even 30+ year old languages had better answers to asynchronous programming than that. Nested callbacks is so backwards its like writing in assembly. Only people whose first exposure to asynchronous programming was Node think it's a valid programming style.
- jMyles 9y ago> Nested callbacks is so backwards its like writing in assembly. Only people whose first exposure to asynchronous programming was Node think it's a valid programming style. It depends on precisely what structures you include in your definition of "nested callbacks" here. I think that the callback and errback chains on the Deferred object in Twisted is basically the perfect abstraction for the flow control they represent.
- mpweiher 9y ago> Nested callbacks and even Promises are not the "right model/style" by any measure. Of course not.
- hitgeek 9y agosame example with async control flow library and node style callback conventions function getUserInfo(callback) { async.parallel([api.getUser, api.getFriends, api.getPhoto], callback) }
- askmike 9y agoEven with a library like async there will be so much boilerplate, since you need to handle (or pass) errors every step of the way. With async/await you only need to deal with errors at the level you actually care about them (using the language build in try/catch block).
- yesbabyyes 9y agoIn the example above, any error will be passed to the callback sent to getUserInfo - i.e., where you care about it.
- samblr 9y agoAgreed there there is boiler plate - but with use of live templates in webstorm (or snippet in VS) - I find it getting easier.
- tracker1 9y agofunction getUserInfo(cb) { Promise.all([ api.getUser(), api.getFriends(), api.getPhoto(), ]).then( ([user, friends, photo]) => cb(null, {user, friends, photo}), cb, ); }
- hitgeek 9y agoI do this with so many libraries now. especially in the react-native ecosystem. I just prefer node style callbacks, so I wrap libraries with promise based APIs with callback based wrappers. hopefully i'm not the only one.
- tarr11 9y agoThough the article doesn't mention it, the "alternative" is Reactive programming (via RxJS) I think for the typical UI application developer, async / await can provide easier readability and debugging, but at the cost of some expressive power and conciseness. Now that chrome supports async / await in the debugger, it's almost certainly the best choice, compared to promises and callbacks. In the redux world, you can see this by comparing redux-observable [1] (reactive / rxjs) with redux-saga [2] (generators) Rxjs requires a deeper understanding of Reactive programming. Once you understand it, you can write very powerful expressions in a few lines. But debugging is tough and it doesn't translate well for your fellow developers. [1] https://github.com/redux-observable/redux-observable https://github.com/redux-observable/redux-observable [2] https://github.com/redux-saga/redux-saga https://github.com/redux-saga/redux-saga
- jrs95 9y agoOnly downside I've found with this is that Observables feel like more of a pain to test, since your logic gets more tightly coupled to your I/O. Or at least there's more complexity involved in the relationship between I/O and data manipulation. I used them for a Node project via RxJS and ended up just switching back to promises, as it wasn't complex enough of a project to really see much of a benefit from Observables.
- alexkoeh 9y agoAlong with that, generators (redux-saga) are amazingly simple to test.
- rounce 9y agoThe only difference between promises and observables are multiple-emission & cancelation, they shouldn't be any more complicated to use or test than promises. Often I find the main complicated part is the source, everything else is just filter/map functions, sometimes to more streams. All of these individual units are more easily described/tested in comparison to the entire chain (and even then you are only caring about the ends of it).
- tracker1 9y ago
- imperio59 9y agoAnyone else bothered by the utter lack of semicolons in the example code? :D
- kjsthree 9y agoLet go and let god. :D
- strictnein 9y agoYes It's like reading English without periods It's frustrating
- askAwayMan 9y agoIf you've got a halfway decent linter, semicolons are just clutter. https://eslint.org/docs/rules/no-unexpected-multiline https://eslint.org/docs/rules/no-unexpected-multiline is the sort of thing that makes semicolon free style practical.
- Bahamut 9y agoThere is one situation where semicolons are important - when concatenating multiple script files & using IIFEs. This subtlety is being obsoleted by ES modules & the build tooling around them, but it is a nasty bug that has sucked many an hour away from frontend devs whenever it is encountered.
- krono 9y agotry catch try catch try catch try catch try catch try catch try catch try catch
- marcofatica 9y agobetter than then().catch().then().catch().then().catch().then().catch()
- coldtea 9y agoOr, you know, a single try catch. Or several of them. At any level you like and fits the problem. And no lost exceptions.
- krono 9y agoYou're right and async await is much nicer but all the try catch blocks are slowly getting to me
- davnicwil 9y agoI heard Doug Crockford talk about how he doesn't think async/await is that great an idea on a podcast a while ago. His argument was that it's an unclean abstraction - it gives you access to 'features' of synchronous imperative syntax (lines in a function always execute in order, try-catch blocks, etc) but it remains conceptually and literally promises all the way down. Therefore, all await 'calls' are really non-blocking at a global level, yet they appear blocking to the local lines of code inside the same async function. This is liable to cause confusion - particularly for beginners or occasional visitors to JS who don't fully grok or have the concept of promises top of mind - but really for everyone. I've used async/await a fair amount in production code now (with babel) and while it does make some code a bit cleaner, honestly it is often to the detriment of understanding it when you come back to that code it in a few weeks. I've made plenty of stupid mistakes where the two 'faces' of the abstraction don't marry up, and it's frustrating. More and more I'm inclined to just use promises, even when I have the choice of async/await - call a spade a spade and get on with your day. The article talks about promise chains getting complex and hard to read. Well, if this is the case, maybe it's your code or logic flows in general that need to be cleaned up, and changing the syntax to flatten the structures is actually just a sticky plaster over that.
- lexicality 9y ago> I've made plenty of stupid mistakes where the two 'faces' of the abstraction don't marry up, and it's frustrating. Would you mind sharing any of these?
- davnicwil 9y agoI'm talking really stupid, small, frustrating things, mostly at the interfaces. I read a return x at the bottom of one function and call the function elsewhere and try to use x, but oh wait no.. it was an async function so I've really got Promise<x>. Generally it's just because of the leaky abstraction making very fast mental mapping of code a bit harder.
- hdhzy 9y ago
- dlbucci 9y agoI was super excited about async/await when it first came out. I hadn't really understood the point of Promises, but async/await looked simple and useful. However, I recently started using async/await in TypeScript, and the result seems to be try/catch statements everywhere. Code using async/await seems to be more verbose and unruly than just sticking to Promises, which I now appreciate the elegance of much more (callee convenience/error delegation). I think I'm just going to stick to Promises for the time being, until I see some hidden usefulness of async/await syntax (which could totally happen. It took me a long time to realize how awesome promises are).
- zackify 9y agoYou can abstract your try / catch logic to a higher level to fix this easily. make all promises extend from a base promise function that either returns a result, or null (or an error, you can make it more complex). Then, let data = await customPromise() if(!data) return this would be better in my opinion that try catch everywhere. Inside that higher order promise you can catch and handle errors.
- pier25 9y agoI'd rather user try/catch than endless chains of then().
- sharemywin 9y agoandThen()...andThen()...andThen().andThen().andThen();
- yarg 9y agoI really don't see the need for such a syntax. If I say: Foo foo = someFoo(); ... String content = foo.getContent(); I really don't care if foo was returned when I called someFoo(), I only care that getContent() is not attempted until after foo is defined. There's little reason for me - in this situation at least - to need this async functionality to be explicitly stated. The biggest problem I see is a when you use this functionality in a loop of independent executions (when the results of any given loop don't depend on the results of prior loops): for(Foo foo: asyncFoos()){ Foo foo = someFoo(); ... result.add(foo.getId(), foo.getContent()); } The problem here is that the use of a for loop will introduce latency, potentially dramatically increasing execution time. Now you can either replace this "for" with some sort of "for-each" type of block, or you can go async all the way and treat "for" as "for-each" and any referenced result of a prior iteration as yet another async value. That covers sets and singletons, I imagine that any other situation that needs to be dealt with can also be covered on the compiler side of things with only minimal changes to syntax.
- whipoodle 9y agoYou may not care, but the computer does!
- miguelrochefort 9y agoWhy doesn't the computer do it for me?
- whipoodle 9y agoAgreed.
- yarg 9y agoPerhaps it does, but my point was it does not need to. The only thing that needs to be ensured is that calls to the object are not issued before the result is returned and assigned - for all code that occurs between the call to get the object and the first actual access of the object, it does not matter.
- 9y ago
- fergie 9y agoNot a gigantic fan of async/await for the reasons that others have mentioned here, but that said it provides the only sane out-of-the-box way for JS to handle loops that contain callbacks/promises.
- miguelrochefort 9y agoI much prefer observables to tasks...
- jaequery 9y agonot too long ago, async used to be the "thing". and now sync is the new "hot stuff". a bit ironic but at the end of the day, simplicity always wins.
- stevedekorte 9y agoAsync/await can be used to implement coroutines, but IIRC only if you wrap all of the code that might be called in async/await as well, and this catch makes them practically useless. Why not just provide proper coroutines?
- hdhzy 9y agoBecause people don't need coroutines, just a simpler way to write async code than callbacks and manual promises. While we're at it why coroutines? You can implement coroutines, generators, even try/catch and return given continuations [0]. [0]: https://curiosity-driven.org/continuations https://curiosity-driven.org/continuations
- atombender 9y agoI wish the await syntax was inverted; instead of waiting for an async function, let all async functions await by default. In other words, I wish this worked: let foo = myAsyncFunc() foo.bar() Why? Because that's the common case, and the caller shouldn't care whether the function is async or not. If you have a non-async function that you want to change to be async, or the other way around, then you have to change every single call site. The case where you want to store a promise or wait on multiple is actually the edge case, and they could have reused "async" here: Promise.all([ async myAsyncFunc(), async someOtherAsyncFunc() ]); Bonus functionality: If you do "async foo()" and foo() isn't async, the compiler could still ensure that it provided a promise. Sadly, it's too late for this, and everyone's code will suffer as a result. It's way too easy to forget to add "await" to an async call, and doing so leads to failures that can be hard to track down.
- mo_po 9y agoJavaScript often feels too verbose. That's why I like to use elm whenever it is possible.
- singularity2001 9y agoI wish your comment gets upvoted to national news. Those async antipatterns are worse than the 'goto' statement. Time for programming languages to add the 'async' keyword on the caller side.
- chronial 9y ago> If you have a non-async function that you want to change to be async, or the other way around, then you have to change every single call site. At least in the direction non-async -> async, that is clearly a good thing, since you are changing semantics a lot. This code is always safe: x = globalObject.a functionWithoutSideEffects() gobalObject.a = x + 1 While this is not: x = globalObject.a await functionWithoutSideEffects() gobalObject.a = x + 1 > It's way too easy to forget to add "await" to an async call, and doing so leads to failures that can be hard to track down. This a situation that could clearly be improved with better detection of these cases. Or maybe it should be completely illegal to call an async function without a keyword, so you have to do either `await func()` or `promise func()`.
- mvindahl 9y agoI really like the succinctness of async/await, and I feel like it's the last step of a long journey. The single threaded callback based style of Javascript has always been a mixed blessing. It has allowed for great performance, and it gives better control that having to juggle threads, but it was all to easy to get into deeply nested callback hell. If you see design patterns as indicators of language smells -- and it's a useful perspective IMHO -- then in this case callbacks were the smell and the various promise libraries were the design patterns. It's a nice thing that promises and their async/await sugercoating have fairly rapidly made it into the core language. Now, for the one thing that I don't like about async/await: it's deceivingly simple and it's bound to fool both novice programmers and programmers arriving from threaded languages. If you don't understand the underlying concept of promises, you'll easily end up writing suboptimal code (e.g. executing stuff in sequence which would be better executed in parallel). Still, a net win.