Certainly, what was referred to as "clever" in this video I have very different words for ;)
@Seedzification6 жыл бұрын
yes his one liner mess, I wouldn't call that clever at all
@zeeeeeman6 жыл бұрын
Agreed. A one liner in ternaries is impenetrable to console.log. Good for code golf, not for real world.
@JohnWick-rc9qq6 жыл бұрын
Functional programming relies heavily on ternaries and recursion as it favors expressions over statements. They're very useful for the real world. It just takes a little getting used to if you've used `if` and `for` your entire life. It' just like learning any language feature -- you're not comfy until you know it.
@electroaddiction5 жыл бұрын
@@JohnWick-rc9qqIf every developer takes a little to get used to, I wonder the amount of time wasted in the end.
@AngusMcIntyre6 жыл бұрын
Replacing loops with recursion is exactly the sort of behaviour that async/await is trying to avoid - the transpiler emits this code so that you don't have to. Recursion is harder to reason about and you can introduce all sorts of fun performance issues. For example - how many arrays have you created there? Long story short - you rewrote the async/await code with more characters, more calls and more conditions. Would you write for-loops with goto? 😉
@victorb95036 жыл бұрын
Where I'm working on right now (and in most previous gigs actually) we are used among other things to write API tests which by nature are async: do this to the db, make a http request, check how the db changed after that request was completed successfully, etc. And we used to do that with either Promises or the "async" js library. When async await got into the lts version of node.js I took one of the test files and converted it, then looked at the diff. Not only was far fewer lines (I think it was a 2x reduction in total lines) but the identitation was kept to a minimum and the lines were easier to follow and read out loud.
@hamaralsilva6 жыл бұрын
This video can be renamed to "how to get things worse by using promises instead of async/await". 😂
@rudde72516 жыл бұрын
This is proof JavaScript developers have the highest occurrence of hipsters.
@Assassunn6 жыл бұрын
But async DOES return a promise, and await waits a promise is resolved while stopping the execution of the following instructions (which wouldn't be possible using then() ! and that's the power of async/await, not a sugar syntax feature)
@gillianbc4 жыл бұрын
I can see the advantage of this when you put the generic waitForEach() into a utility module. In your code module, you only then need to define the function you want applying to each of the items. It does make it cleaner and more readable
@ProgramArtist6 жыл бұрын
I am not against promises and don't have any strong opinions to prefer one over the other but here is what I think about the things you said in the video: The complexity of the code is not measured only by how nested it is, it is also measured by the number of things you need to keep in mind while reading the code itself. 13:32 you said you had to think about the code and after you figured it out it felt simple. For me, a sentence like this means the code smells. I prefer the code to read like a well-written book and not like a smart poem. What I mean by that is that in well-written poems, you need to stop and think about every line and understand the meaning of it and how it contributes to the idea of the poem. In the book, on the other hand, you will read much more but the idea will be much more clear to you and you will get it faster. Also for me, a code with promises looks a lot like a code with streams (like in RX for example), and it sometimes feels like reading a very long sentence with many ideas in it, you get lost in the middle and go back to re-read from start. I prefer reading short sentences one by one and breaking the ideas apart. Probably you can refactor async/await code to promises in a good and understandable way, but it is harder to do so and there are much more ways to get it wrong.
@ErnestGWilsonII6 жыл бұрын
I see folks juggling to avoid a flat out admision that async/await is better on long complex multi-step asynchronous programs. Mixing Promise and Promise All with a main driver script that uses async-await is a huge improvement to code readability. First learn callbacks, then write everything Promise / Promise All style and then graduate to writing the main body of your program in async/await style, this is really satisfying and super readable allowing anyone following in your footsteps to easily reason about your code, a clear sign that async-await carries less technical debt.
@AjdenTowfeek6 жыл бұрын
First of all, big fan of your show, but you guys really struggled trying to argue that async await is useless in this episode and imho failed miserably ;-) From my experience simplicity and readability is always preferable over complexity especially in larger organizations where the skill may vary quite a lot among the developers. Developers being proud of being clever when performing such basic operations/tasks is usually a red flag. Nobody in their right mind can say that the second (sql) sample is more readable and more straightforward after the refactoring. You guys literally made me laugh when you couldn’t motivate why the refactored code was better other than “I like it because it’s recursion, it’s neat because it’s recursion, it just feels *arghh*”, really? The code became more complex after the refactoring and the argument that it makes you think harder to write that code comes up short imo. You said it yourself, “it’s hard for me to figure it out, but once I have it, it’s simple”. Honestly which code would you prefer to debug and find bugs in? Something you understands immediately and could write up without even thinking or something that feels so great because it’s recursion but you don’t understand it right away?
@funfunfunction6 жыл бұрын
We did not argue that. :)
@danilocecilia78316 жыл бұрын
for me being a 'noob' , as my point of view, the first example is way better understandable and honestly I am still trying to figure it out the recursive refractory solution, feel so stupid right now... =(
@iandashb6 жыл бұрын
Async/await removes a lot of cognitive load from the Devs trying to work out what the recursive function is doing in the Promise rewrite. The asynchronous/await would be my preference, especially for debugging, but interesting point about error handling....
@JoshuaMuniandy6 жыл бұрын
I agree. As a noob programmer, I always find Promises daunting. But when I started learning Async Await, I found it easier to code, read & debug. I found that 2nd example of the SQL code was way too complexed esp with the recursion.
@adambechtold67416 жыл бұрын
They also missed an obvious opportunity to run their processUser function in parallel. Instead of calling await in the for loop, they could have collected those promises in an array with awaited Promise.all(). In many cases, this would have been faster than both their examples.
@TheMasterpikaReturn6 жыл бұрын
Some thoughts as I watch the video. Also, I'm a functional programming enthusiast, and use quite a lot of haskell in my free time, so there will be a lot of Haskell comparisons. 1- Is async await useless? I'm torn on this one. These days I mainly write Haskell in my free time, and async-await feels like an ad-hoc version of do-notation (much like promises feel like an ad-hoc version of a generic monad typeclass). So I wouldn't say it's useless, but the fact that they had to add an ad-hoc syntax for an ad-hoc construct kinda bothers me. 2- I think code with promises would be much clearer if "if" was an expression instead of a statement... 3- Couldn't you get rid of the nesting by just wrapping `server.getMaxNumberOfBurgersPerDay()` in a Promise[.resolve()], so you can call `then` after the outer `then`? (so it becomes server.hasBurgers().then(.....).then(....)). 4- I don't agree with the "in your face" thing: personally I think that asynchronous flow should be as implicit as possible. For example, haskell's I/O is non-blocking, but while coding you don't really think about it. In my opinion, we should be able to program in a style that looks like synchronous code, but can be asynchronous, and it should be the language's job to transform between those representations. It's a lot like callback hell: it's pretty much Continuation Passing Style, and you can do it manually... but why do it when you can have a CPS-like Monad (like haskell's Cont") and write code that doesn't really look like CPS (albeit with the occasional callCC)? 5 - in the for loop example, i'd think you could so something like `users.map(user => await processUser(user.id))`, can't you? 6 - wait for each looks like a fold dying to come out. Maybe some sort of `compose(foldr(then), map(processFn)`? [where then(a,b) = a.then(b) 7- also, array destructuring feels like ad-hoc pattern matching :D although I guess it's not as ad-hoc, cause you can use it with objects too? 8- the stuff about error handling and aggregating data to me feels more of a consequence of the fact that Promises are very functional(as in functional programming) of a construct, and Javascript doesn't have very good support for functional programming, which means that there's quite a bit of stuff that you can't really do without it feeling weird or convoluted. It always feels like JS features are kinda thrown there, and don't really compose very well with each other. But hey, that's just my 2c, feel free to disagree
@dungandonuts6 жыл бұрын
Pika ^_^ you had the same idea as me that the last example could have been a fold/reduce! When I first saw destructuring it did remind me of Haskell's patten matching and here is the same as the x:xs pattern. It's kind of a shame that JS doesn't have a concept of monads because Async/await basically is just do notation but only for promises.
@dimaorols81436 жыл бұрын
In my opinion async/await only offer a syntactical sugar on top of promises themselves. - Writing Promise.all(users.map(user => processUser(user.id))).then().catch() is better as it would resolve items in parallel and fail on failure. - Generators can be similar if considering that every generator is pausable and can have it's own state during execution. That can be recreated with a curried function where it's inner function uses closure declarations for internal manipulation. - As for curiosity you could look at LiveScript transpiler. It doesn't use async/await and it's closer to functional programming than CoffeeScript: livescript.net/#
@TheMasterpikaReturn6 жыл бұрын
Oh I forgot about Promise#all. That's most likely the best option. Generators... eh I always found them kinda clunky to use. Also I already know of livescript, and it's one of the options that I considered before learning haskell, but nowadays I can use haskell on the front-end too (either ghcjs or purescript, which is very similar), so I never looked into it again.
@artgreg22966 жыл бұрын
Especially agree with your point 3. "then chaining" was meant to avoid nesting
@justvashu6 жыл бұрын
Art Greg the thing about then chaining is that it really starts to break down when you depend on the result of a previous promise or many others. Then you need to start nesting and have to get clever with the flow of code in order to pass values along or follow conditions. It also makes the code a little harder to break down into simpler units for testing because all the promises are coupled with the one that provided it’s required data. There are ways around it of course. The thing I like about async-await is that I can unit test the code without promises if it’s more practical.
@BrandonBey6 жыл бұрын
Oh no, is MPJ saying you should write clever code again :( Code styles are mostly a personal preference thing - if you are writing for yourself, go ahead and be clever. If you are wiring code that is to be shared - making it less clever and more clear is always better.
@Sworn9736 жыл бұрын
6:37 "it is not to bad". Everything in one big line, lambda, with nested promise with a lambda with inline if, with another promise with another inline if. Definitely is not to bad, is insanely bad. IF the main point of the away is to create better code, Yeh it works, but as everything else in programming if not used with sanity, it will end up with nightmare mess. Even in functional programming breaking down those inline nested mess will make it more clean. 15:05, return a inline calls will never be "clean". As you said, "We return the query, than we process the user" ops, Return, end of function. So it is not so clean and have traps just "await"ing for unaware people. The actual order of the inline call can be trick. Specially if you throw some lambda with recursion. if you write like you tried to say query get users process that is clean, And you return what ever is needed;
@emretekince6 жыл бұрын
of course it's useful. simple, clear and readable code!
@AndreasWindt6 жыл бұрын
Exactly! As satisfying as it can be to come up with a solution like the one in the second example, it might become very hard to grasp what it does at first sight (one could say, »it was hard to write, it has to be hard to read«). I'd definitely prefer the async/await form of it for maintainability!
@hajimeyamasakivukelic72356 жыл бұрын
Here's a missed opportunity. Throw exceptions into the mix (pun intentional), and suddenly promise-based code look much better. Also, the example code has bad separation of concerns in general, and promise-based solution tend to push you towards solving those issues as well. Overall, async/await encourages some bad patterns if you look at it carefully.
@vlad9816 жыл бұрын
TL;DR, no it;s not useless )
@Ozziepeck116 жыл бұрын
Vlad it’s
@u4yk6 жыл бұрын
So... Betteridge's Law, then...
@LoveLearnShareGrow6 жыл бұрын
I absolutely love that I scrolled down to see all the comments were about READABILITY. I'm only a junior programmer, so I can't say what senior programmers will find "easy to read" but recursion (as much fun as it is to write) is *horrible* for readability. And the main thing I hate about promises is that it keeps breaking up the scope so that I have to do weird tricks to pass data from the top to the bottom. Async/await solves those problems, at least when you don't need fine grained try/catch blocks. It's usually very easy to read and very easy to pass data where it needs to go. I love it.
@nachovalera52176 жыл бұрын
I like your video because it shows to me async/await is life❤️ even if you don't want to prove that I understand what happens behind async / await and I think it is a great improvement to the lenguage because it helps you solve real tasks faster and helps you think at a higher level I like that type of promises hell for educational purposes but not that much for real life anymore having async/await
@joaofnds6 жыл бұрын
10:18 why not: function proccessAllUsers() { const sql = 'SELECT id FROM users' db.query(sql, []) .then(users => Promise.all(users.map(proccessUser))) }
@funfunfunction6 жыл бұрын
Because that would change the meaning of the code example. The example illustrates sequential promise processing.
@GodOfMacro6 жыл бұрын
and what about db.query(sql, []) .then(users => users.reduce((q, u) => q.then(u => processUser(u.id)), Promise.resolve()))
@funfunfunction6 жыл бұрын
Yep, that works great!
@sney20026 жыл бұрын
There is an error in waitForEach which makes it run in parallel instead of sequentially. the next call to waitForEach must be inside of a function call: processFunc(head).then(() => waitForEach(processFunc, tail))
@modernclics6 жыл бұрын
I totally agree on using Promise.all despite of what the original async / await code was trying to prove. IMHO the proposed solution on the video looks unnecessarily complex. In the end Javascript is asynchronous and the most coherent way to wait for a set of promises is using Promise.all. Maybe Matt and David should have consider a different approach for the video like: "Javascript is asynchronous, don't try to make it look like it isn't" and propose simpler algorithms using promises. Disclaimer: This is not a rant, I'm a big FFF fan. :D Cheers!.
@wmhilton-old6 жыл бұрын
At 14:00, won't `waitForEach` silently swallow errors generated by processFunc?
@maagiapall6 жыл бұрын
One of the big reasons I much prefer the async/await version for getHamburger is this: imagine you're seeing this function for the first time, in the middle of a random codebase, no documentation, maybe the function isn't named as clearly... And you need to figure out exactly what the function returns. In the async/await example, I can scroll right down and see that it returns defaultBurger, or loadBurgers depending on the branching. In the case of promises, I have to sort of start following what's being returned at each level of indentation and slowly work my way to the end to see what's being returned. I could technically skip to the deepest level of indentation, but in that case I might miss some logic that happened before. This might take 2 seconds or 20 seconds longer, but the point is, it's time that you have to take to figure stuff out, as opposed to the async/await version which reads like a book. The more we can avoid these 2-20 second distractions, the less tired we will be 2, or 4, or 8 hours into the work day. And the more productive we will be as a result. So even if the code is only slightly more readable, to me that's a big win.
@RazvanLaurus6 жыл бұрын
async / await is certainly an improvement. It doesn't require as much boilerplate as promises do (or callback style) and it makes the code easier to reason about, thus more maintainable. As for the conclusion of this episode, that functional recursion is an alternative, do keep in mind that recursion is not "safe" in JS. Not all platforms support tail call optimization, hence with long lists (high depth), there's always a risk to run out of stack.
@SimonScarfe6 жыл бұрын
Great video! Is there any light at the end of the Proper Tail Calls / Tail Call Optimisation tunnel in JS-land? At the minute I'd hesitate before reviewing any code using recursion with arrays of unknown size, unless I was developing only for Safari.
@HermanWillems6 жыл бұрын
There is Tail Call Optimisation is a specification of ES6... Not sure about the details though jsperf.com/tco#chart=bar
@bloodaid6 жыл бұрын
This reminds me a little bit of the whole "assembly programming is better..." argument people used to have once high level languages started to appear. Async / Await are simplifiers. USE THEM!
@matthewbarbara29166 жыл бұрын
You said something like this one on the burger code "This code is horrible and it has a very bad architecture". Given that it would not take much of your time to polish and refactor such code, I recommend you to always show a quality code for the sake of the followers who lack the coding quality. Keep it up
@funfunfunction6 жыл бұрын
It's also code about burgers. :) This is example code bent to illustrate certain points. Real production code has massive amounts of noise (and is honestly often not perfect either) and would not serve to achieve the goal of the video.
@majorbadidea6 жыл бұрын
Fun Fun Function yes but the lets could have been consts and the formatting could have been prepared
@wonderingAroundtoNoWhere6 жыл бұрын
Methew Barbara i have to see the code, but i am kinda sure you are not right, because that's not how it work, you keep the noise out and focus on the topic, it is hardest things to do, i am learning my self hard way which code to use and which one not to use. And when it comes to JavaScript the whole world is up side down, because things are so inter connected, So in short they probably use the right code for the topic which they are covering.
@chrsolr6 жыл бұрын
Quick question. How's easier/better to do error handling with promises? How about try/catch? Am I missing something?
@modernclics6 жыл бұрын
It's actually really simple and it encourages you to catch errors: someAsyncFunction() .then(result => {}) .catch(error => {})
@TedHopp6 жыл бұрын
David was struggling to find the words to laud recursion. Perhaps this (anonymous?) aphorism will do: "To iterate is human; to recurse, divine."
@vzsshadow31416 жыл бұрын
at 12:00, if i see well then recursive call inside of then, right?
@brenton85686 жыл бұрын
The implementation of `waitForEach` isn't equivalent to the async/await example. In the async/await version the `processFunc` is only called for each item *after* the previous item has been processed. But the `waitForEach` version calls `processFunc` on every item in the list immediately. This is because the recursive case is calling `waitForEach` eagerly instead of lazily. This can be fixed by wrapping the `then` in a function. e.g. `processFunc(head).then(() => waitForEach(processFunc, tail))`. Alternatively, a reduce can be used and the explicit recursion avoided entirely. e.g. `const waitForEach = (processFunc, items) => items.reduce((acc, item) => acc.then(() => processFunc(item)), Promise.resolve());` This has another advantage - it returns a promise that is resolved when all of the processing is complete! (It also avoids people complaining about the potential stack overflow with recursion ;))
@AngusMcIntyre6 жыл бұрын
Brenton Alker I believe this is incorrect. The n+1th item is only processed when the nth item has completed. This is achieved by start a continuation with .Then(). Continuations do not execute until the antecedent completes. It is not just a fancy wait. It is synonymous with Bind in the monad pattern.
@gasjack6 жыл бұрын
I had the exact same thought. In fact, I don't think the refactored example would even run, since .then() takes a function, but in the refactor, it is handed the result of calling waitForEach (which is a Promise)
@brenton85686 жыл бұрын
Angus, I'm not sure which part specifically you believe is incorrect. The async/await example at 09:00 behaves as you say - it only processes the n+1th item after the nth item has finished processing. But the "waitForEach" version at 12:00 will start processing all items immediately and doesn't care when they finish. As my comment says - if you want the same behaviour as async/await, you need to delay the processing by wrapping it in a function (continuation). ".then" doesn't create a continuation, it takes one and runs it, the code shown doesn't give it one it gives it a promise (which is already executing - promises are eager). Here is a repl.it session with all 4 versions, you can see the difference in behaviour in the buggy version compared to the others - repl.it/@tekerson/Is-async-await-useless
@brenton85686 жыл бұрын
Rasmus Vestergaard Hansen, you're right, yes, it is passing it a promise instead of a function, luckily (I guess) the spec says that if you pass anything that's not "callable", then it will just treat the identity function as the argument (ie. that step in the promise chain will just pass its value through) - www.ecma-international.org/ecma-262/6.0/#sec-performpromisethen e.g. This produces "Hi!" even though the first `.then` isn't a function. `Promise.resolve("Hi!").then(false).then(console.log);`
@AngusMcIntyre6 жыл бұрын
Brenton Alker I see it now. Thanks for the explanation. Well spotted!
@smilebagsYT6 жыл бұрын
First thought before watching: No, no it isn't. Let's see if I change my mind!
@smilebagsYT6 жыл бұрын
Well, I didn't change my mind but it was a good look into why I believe async/await syntax is beneficial. Great video, thank you!
@benjaminmosnier6 жыл бұрын
I was exactly thinking this way... but I have to admit the last example showing recursion is very clever
@vlad9816 жыл бұрын
yeah, I think it's too much clever) We should use easiest solution possible, and async\await is definitely makes code easier to reason about.
@AngusMcIntyre6 жыл бұрын
Iron Candy the language shouldn't be holding your hand?!? That is exactly why we have languages. Would you prefer doing it all in assembler?
@smilebagsYT6 жыл бұрын
I must disagree. Async/await encourages readable code. Whether the developer decides to write convoluted, long functions is unrelated. Async/await only seems to take away the understanding of what's really happening, only until they try actually doing something with it. You need to understand promises to use async/await effectively. Syntactic sugar is syntax. Syntax which makes things easier is a good thing.
@danilocecilia78316 жыл бұрын
oh... im sorry I didn´t get the explanation of the loadallusers, where that ProcessFn comes from?
@paolodisintegra6 жыл бұрын
Why do we have to hate ourselves and use promises when we can enjoy some simple async await and invest our time on more fun stuff 😄
@illuminatiZ3 жыл бұрын
It is pure intellectualism. but when you must code and have no time, it is time to code async/await.
@sohamchauhan43725 жыл бұрын
Variables in promises are scoped and not available to outer scope. For example, const user = User.find({id: 'some id'}); const admin = Admin.find({id: 'some id'}); if(user.name === admin.name) { // do something} else{ // do something else} How can this be implemented with Promises?
@fishfpv99165 жыл бұрын
What is your thought on combining promisies and asynchronous await. I have used it to push several network requests to an earray then calling await on the Promise.all before moving on the the next step of the function.
@lnr1246 жыл бұрын
Is there a typo on line 11 of your rewrite? Should it be processFunc not processFn? Or I am reading it wrong?
@DavidWMiller6 жыл бұрын
You are, it's the argument, named on line 8.
@OkanEsen6 жыл бұрын
Yeah but I guess the purpose of the video wasn't really about "correctnes" but rather to get the point straight and therefore more like pseudo code.
@ChrislyBear6 жыл бұрын
Probably a typo.
@VanjaMk16 жыл бұрын
Yeah, that tipped me off too. A typo. Good thing I searched the comment thread before posting it ;)
@benbanda076 жыл бұрын
Where I used async/await the most, is with mongodb, when I need some arrays from differents collections (example: user, appointments, profesional and service) and if I had used promises, I would have had a "promise hell"
@funfunfunction6 жыл бұрын
We discussed this in the forum, and we concluded that it works fine with promises if you do the same thing that you technically do with async await, and just collect stuff in a closured variable: const displayAccountAndContacts = accountName => { let account return fetchAccountByName(accountName) .then(loadedAccount => { account = loadedAccount return fetchContactsByAccountId(account.id) }) .then(contacts => { console.log(`${account.name} (${account.id})`) contacts.forEach(contact => console.log(` - ${contact.fullname} (${contact.job_title})`)) }) .catch((error) => { console.error(error) }) } Alternatively, you can build up the result gradually: const displayAccountAndContacts = accountName => fetchAccountByName(accountName) .then(account => fetchContactsByAccountId(account.id) .then(contacts => [ contacts, account ])) .then(([ contacts, account ]) => { console.log(`${account.name} (${account.id})`) contacts.forEach(contact => console.log(` - ${contact.fullname} (${contact.job_title})`)) }) .catch((error) => { console.error(error) })
6 жыл бұрын
I find generators with "co" library to be more powerful than async-await, as execution can be cancelled after some step. But 9/10 times I rather write an async function, as no additional library needed and would be easier to understand for others.
6 жыл бұрын
Well, most libraries you can implement yourself in a rather quick time. But using something field-tested is most of the time better.
@pepkin886 жыл бұрын
Yes, this. And also nice and intuitive abstractions of yielding arrays and objects. Plus support for thunks, which are just old style Node functions, partially applied with Function#bind.
@allan29326 жыл бұрын
As I see it Promises is the JS functional way of doing async programming. It can be used together with functional techniques like currying and point-free programming. I prefer this style but if you are more into imperative programming async await is probably easier to reason about. The point with the SQL example that used recursion was also that the "waitForEach" function most likely could be found in any functional library and therefore you probably wouldnt have to write it in the first place if you were doing functional programming. If you dont have to write that function then that example get easier to understand than the async await one. Also, in order to have error handling with async await you need to place it in a try catch and that is not very nice code to look at compared to a catch I think. Its true that aync await have less nesting but if you more than 1-2 levels of nesting with your promises you should probably rethink your architecture anyway
@SumTsuiTechie6 жыл бұрын
surprise this comment didn't get a lot thumbs ups
@PeerReynders6 жыл бұрын
Seems some people overlook that having learnt how to wrangle promises is beneficial practice for when you later need to learn how to wrangle observables (RxJS or tc39/proposal-observable). I suspect that the recursive solution at the end was a cliffhanger to talk about for-await-of which is included in ES2018 (tc39/proposal-async-iteration). async function main() { const users = [ { id: 1, delay: 1000 }, { id: 2, delay: 300 }, { id: 3, delay: 500 } ]; for await (const x of users.map(processUser)) { console.log(`done: ${x}`); } } // Output: // start: 1 // start: 2 // start: 3 // resolve: 2 // resolve: 3 // resolve: 1 // done: 1 // done: 2 // done: 3 const processUser = ({ id, delay }) => { console.log(`start: ${id}`); return new Promise((resolve, _reject) => { fakeProcessing(resolve, id,delay) }); }; const fakeProcessing = (resolve, id, delay) => { const resolveIt = () => { console.log(`resolve: ${id}`); resolve(id); }; setTimeout(resolveIt, delay) } main();
@PeerReynders6 жыл бұрын
I never implied that observables are a "one size fits all" solution. I'm not Netflix: Promise to not use Promises - ES7 Observables by Brian Holt kzbin.info/www/bejne/epKmlGuimNCorZo I was thinking more about enriching one's toolbox, broading one's skillset to be more prepared for upcoming challenges. Most button-click examples are just a "Hello World" equivalent and not really representative of effective use cases for observables (though the throttle and debounce operations seem to be popular with click event sources; see "ReactiveX RxJS Manual Overview Flow" example) In terms of UI "drag and drop" would probably be a better example. Processing (and filtering) data coming in over one or more WebSockets at unpredictable intervals would be a much better example but would already require a some basic understanding of working with event streams. Even Ben Lesh (RxJS lead) admits that understanding observables is rarely "easy" in the beginning. kzbin.info/www/bejne/emisnpmDbMSgmdU "The introduction to Reactive Programming you've been missing" by Andre Staltz is one of the better introductions. Observables primarily exist to make dealing with repeating events (event streams) easier. This is accomplished by moving away from "program statements defining a sequence of steps to be taken" and moving toward "data traversing a pipeline of composed operations which transform the data by applying expressions". It's a programming style where you structure the flow of data, not the flow of control. It's a different way of composing logic that relies on composing operations and expressions rather than composing statements. Promises and observables are strangely similar even though they are different: - A promise value/error pushes through the chained handlers as soon it is settled (i.e. it's eagerly evaluated), while observable events are pushed through the operators only when at least one subscriber is listening for events (i.e. they are lazily evaluated). - A promise settling is like an "event" but by design the "stream completes" after that single event - so conceptually it is a "single event stream". Because promises can only be settled once, it is possible to retrofit imperative programming constructs in an async, await, or for-await-of style which tends sweep the promise's asynchronous nature under the rug (for better or worse). A promise handler chain clearly focuses on the sequence of composed transformations that occur when/if the value/error becomes available.
@gillianbc4 жыл бұрын
Sorry if this is a dumb question, but in the refactored code, line 11, what is processFn?
@TheMrChugger6 жыл бұрын
That final recursive solution makes me feel so dumb. I mean I understand it entirely, but I'd never have dreamed of writing it like that. Beautifully done.
@nloomans6 жыл бұрын
Wait... why didn't you use `Promise.all` in 12:30?
@louisvno76496 жыл бұрын
Noah Loomans yea i'm also wondering
@louisvno76496 жыл бұрын
I guess they wanted to replicate exactly the behaviour of the asnyc/await version of the code
@funfunfunction6 жыл бұрын
That would change the behaviour of the code
@ChrislyBear6 жыл бұрын
Question: If you have an async that functiu that uses await and you want to call this async function from elsewhere, do you have to call it using await again? And if so, then the calling function will also have to be async again, right? So, how far up the call stack do you have to sprinkle the async/await? All the way?
@funfunfunction6 жыл бұрын
An async function returns a promise.
@vzolin5 жыл бұрын
So using with is almost obfuscated code is better even if it doesn't offer any performance improvement?
@jasonmcintosh26326 жыл бұрын
Doesn't recusion place a big load on the stack which ends up being much slower and more resource hungry?
@mrjosephDJ6 жыл бұрын
Can you paste a copy of the object that is returned from the db.query just as a example?
@phimuskapsi6 жыл бұрын
One nice thing about async/await is that the async function is automagically wrapped in a then/promise. This means that from a synchronous function you can call an await function and do a then and the async will return data.
@voifusgaton6 жыл бұрын
The `processAllUsers` translation is not correct. `waitForEach` calls `processFunc` over all inputs instead of waiting for the first promise to resolve before calling it over the second input. Additionally, `waitForEach` is not generic, as it will short-circuit if you attempt to use it over an array of booleans, numbers that might be 0, possibly empty strings, or any array of possibly falsy values. Given that it behaves in the way I just described, it might as well just be defined as: `const waitForEach = (fn, vals) => Promise.all(vals.map(fn)).then(() => {});`
@bunglegrind15 жыл бұрын
I prefer the third way: working with asynchronous libraries such as Douglas Crockford's parseq or nodejs async. I tried to implement the example using parseq (hopefully without errors...) - For the sake of brevity, async requests are not supposed to return an error. The function returns a "requestor" - for more info check Crockford website or his latest book. pastebin.com/embed_js/ZEhSP6PU
@LadyofBakerStreet5 жыл бұрын
do we know of a functional promise library that has a waitForEach equivalent ?
@OliverLonghi6 жыл бұрын
You can use functional programming to iterate over returned array (after async/await db consult) as well, don´t you?
@CPlayMasH_Tutoriales6 жыл бұрын
Maybe with simple examples promise-based code is better, but when you have complex logic with lot of edge situations to be handled, then async-await is way better for readability and productivity
@julianpinn50184 жыл бұрын
Conclusion: async/await results in clear and powerful code that is still readable when revisited in the future.
@adrianli77576 жыл бұрын
When I'm trying to decide whether or not one bit of code is better than another. I always ask the question: which piece of code will take less time for a beginner to understand? Sometimes I still use Promises instead of async/await (depending on the use-case), but for most of these examples, I think it fails to show the benefits of plain Promises instead of async/await.
@GreenZ3r06 жыл бұрын
Your db example can be a lot shorter if you use Promise all and array map. ( return Promise.all(db,query(...).map(u => processUser(u.id))) )
@okaybenji6 жыл бұрын
great video! i was convinced async/await was useless, but these examples actually convinced me i was wrong. is there a way to tell, before reaching for promises, that the problem you’re working on might be one async/await is better suited to solve?
@loolog6 жыл бұрын
I noticed there might be a problem with the recursion code in the example starting 10:18, other than the `processFunc`/`processFn` typo, and sure enough it doesn't do what you intend to (probably because of a typo too): gist.github.com/googol/83577cab537656ea9a4d3ea745d5e70f
@gasjack6 жыл бұрын
Yep, noticed the same thing when watching - was too lazy to try it out though :-)
@andzrit6 жыл бұрын
There are some differences in syntax but I thought the key difference between async/await and promises is that await is similar to generator function/yield in such that you can stop and iterate. Await will always return a promise object too, so it really seems like apples and oranges. Am I wrong...?
@MattFarmerdotnet6 жыл бұрын
In my experience the one thing that is easier to program correctly using async/await is when part of your code `throw`s before you return your promise. See this example: // Common methods function getConfig(key) { throw new Error('Key Not found'); } function someAsyncMethod(data) { return Promise.resolve(data); } // Implementations function promiseVersion() { return someAsyncMethod(getConfig('someKey')); } async function asyncVersion() { return await someAsyncMethod(getConfig('someKey')); } // Show Differences // Throws an error, error handler is not called promiseVersion().then( s => console.log(s), e => console.warn(e) ); // Error handler is called asyncVersion().then( s => console.log(s), e => console.warn(e) )
@krzysztofkk69646 жыл бұрын
Why are you processing users sequentially instead of in parallel (that way you should get the overall result quicker) ?
@funfunfunction6 жыл бұрын
Because it's a made up example to illustrate sequential processing. :) In real life, you might want to do sequential processing because not all users could be loaded into memory at the same time, let's say 40 million users perhaps.
@krzysztofkk69646 жыл бұрын
Fair point. Thanks FFF :)
@SherazAli-ky1kn5 жыл бұрын
Nicely explained. But, I was expecting you guy would draw a comparison in terms of performance. Or Its just same in both cases?
@OhhBabyATriple5 жыл бұрын
Poor example at 9:16 adding the await in the for of loop is actually slowing execution. There is no need to wait for the result of the processUser function, therefore we do not need the await. If the processUser function uses an external rate limited service that should be handled in the processUser function rather than improperly awaiting unnecessarily
@funfunfunction5 жыл бұрын
Yeah, but we cannot just change the code to do something else when we are comparing syntaxes. :)
@jonas-mm7em6 жыл бұрын
I'm wondering why your variables are not const instead of let in your first example of async/await? EDIT: As well why not using Promise.all instead of waitForEach function? In the latter you are cascading your async operation rather than running them in parallel with something like db.query(sql, []).then(users => users.map(user => processUser(user.id))).then(userPromises => Promise.all(userPromises)). I find it even easier to reason about, what do you think? Is the cascading supposed to be required by the way processUser is supposed to work?
@funfunfunction6 жыл бұрын
No particular reason for the first one, its just not relevant to the topic at hand. Using Promise.all would have changed the logic of the code. The point of the example is to show sequential processing. :)
@jonas-mm7em6 жыл бұрын
Of course you are right. It is not relevant to the topic at hand. I just wondered if there was a particular reason. :) Alright I get it for the sequential processing, you are right indeed. I didn't think sequential processing was to be kept. Now I get it why your waitForEach function is such a nice way to do it. Thanks for the clarification. ;) Have a great day.
@FirstLast-rb5zj5 жыл бұрын
The issue isn't strictly level of nesting. It's taking a block of code and having to split it. That can add nesting but it can also result in a string of a lot of functions more than you would need with it difficult to divine flow between them. This is what is meant by the spaghetti of callbacks. It's all a moot point. Promises and async/await are neither compatible nor interoperable with callbacks so you have to stay with callbacks. They both actually do things slightly differently to callbacks and if you keep using them instead of callbacks as though they're the same you'll end up writing broken code. Promises and await/async are for the parallel processing. Your wait for each function can actually break if it's not using callbacks if first come first serve. However you're just looping the users one by one.
@giovannipds6 жыл бұрын
Hello! Just watched this while going to work by bus. So yes, it is useful, and it's was very interesting to see that, if we don't arquitecture our code well with promises, we can easily fall into a callback hell. That for with const bugged me. Does that really work? 9:46. I've always thought that there it must be a let, 'cause it will use the same var scope over and over again. Am I wrong? Why const there??? Howww?! O.o
@PauloBrumatti6 жыл бұрын
Giovanni Pires da Silva it actually recycles the variable every time the for runs, not only it's value. Therefore, the variable isn't "reattributed" on every run, rather it's trashed and created and that's why const works, since it's block scoped
@giovannipds6 жыл бұрын
That's very weird, I always thought that the variable was created once and its value was only reset at each loop. O.o I wonder how many languages use the same approach now... ps.: thank you!
@PauloBrumatti6 жыл бұрын
Giovanni Pires da Silva that was how "var" used to work. Since let and const are block-scoped, they only live while their own block lives.
@giovannipds6 жыл бұрын
Right. Thanks again. =]
@LucasVieira426 жыл бұрын
Don't be clever, write stupid code. You can write stupid code using Promises, I think you missed the opportunity to show it on this video. Stop this ternary madness (:
@bjornkihlberg21036 жыл бұрын
async/await is an idea from Haskell. You could imagine something that is to I/O what async/await is to promises. Let's call it do/read or something along those lines. The same way async/await allows you to see on a function if it somewhere contains promises, do/read would allow you to see on a function if it somwhere contains I/O. Example async/await async f() { const x = await api.getUsers() } Example do/read do f() { const x = read console.readLine() }
@DavideOrazioMontersinoPlus5 жыл бұрын
Also ternary expressions. Might be useful sometimes, but if you are indenting your code, then your expression is too complex and plausibly could get changed in the future. Probably you'll have to change to an if / then statement (for declaring variables, for breakpoints and debugging, whatever).
@carloscaleroflores43586 жыл бұрын
The act of destructuring the array is called Pattern Matching just a little FYI there :)
@ThePapanoob6 жыл бұрын
Why arent you using Promise.all? This would make alot more sense in the db example
@dipunm6 жыл бұрын
I wonder if you noticed that your last example was not the same? The functional version runs in parallel because you execute the recursion before the then call and pass the Promise to the then function. We could achieve the same with async await by using `await Promise.all(users)` but if there are side effects, you have a bug that you potentially missed.
@giladierez6 жыл бұрын
Agree there seems to be a bug there.... If you think about, this is actually a good example why Async Await are needed in the language :)
@qwarlockz80175 жыл бұрын
Yeay! I love the opening ! I have seen a few eps where it was not there and I sooo always miss it!
@LarsGyrupBrinkNielsen6 жыл бұрын
7:15 getHamburger with error handling and less nesting: const getHamburger = (server, defaultBurger) => server .hasBurgers() .then(hasBurgers => hasBurgers ? server.getMaxNumberOfBurgersPerDay() : Promise.reject()) .then(maxBurgers => getNumberOfEatenBurgersToday() < maxBurgers ? server.loadBurger() : Promise.reject()) .catch(() => Promise.resolve(defaultBurger));
@JlNGLEZ6 жыл бұрын
I'm glad you mentioned error handling - I find when making any more than 3 calls within an async function is where it shines - wrapping the initial function call in a try catch for error handling - it's very easy! I do agree with the fact that it does steer people away from understanding the true functional programing - but when you're in a team of developers I'd argue that it's easier to read and understand the async/await decorators then it is to read and understant a recursive functional statement - love the videos though keep it up!
@sirynka6 жыл бұрын
[head, ...tali] = [0, 1, 2, 3] i kind of understand how does it works, but where can i find more info about this strange and nice looking thing?
@adammoefoe6 жыл бұрын
Once I understood async/await and used it, I found it hard to use normal promises without hitting myself. I enjoy the synchronous look, and that I don't have to ever explicitly create promises because async guarantees promise return. The only time I use .then and .catch now is when I'm dealing with a single promise, otherwise it's try catch with async/await.
@simoneicardi39676 жыл бұрын
[OT - Maybe an idea for next episodes] - ES6 Keyed collections: Maps, Sets, WeakMaps, WeakSets. What do you think about? Could it worth talking about them? Tnx!!
@andrejbartko6 жыл бұрын
I don't know. perhaps I'm biased. I buy it in the first example although I would write it differently. In the second example, the recursion is just easier to read for me. If I see more than 3 times async / await in the code it just gets harder to figure out the logic, while if it is done as promise chain or observable pipe it is clear.
@TheCodingTeacher6 жыл бұрын
Homie, that rewrite not using the ternaries is not AT ALL equivalent to the initial code. You are returning defaultBurger from inside the promise, which is NOT the same as returning defaultBurger from the root scope of the async function, as it was in the beginning. In essence, the original code had fallback statements returning defaultBurger, but yours evaluates to undefined.
@josemaenzo6 жыл бұрын
Is server.loadBurguer() synchronous?
@vladyslav45 жыл бұрын
about waitForEach example - if you head ever happens to be 0 (which is absolutely valid element in arbitrary array) you code breaks. same for empty string, null, undefined or false. any of those could be elements of arbitrary array
@vladyslav45 жыл бұрын
so since you don`t have any way of checking end of array by looking at head, you actually have to use index and array length.
@ekhmoi45526 жыл бұрын
I would curse so bad if I took over a project where previous developer decided to use recursive functions instead of readable code just to feel "satisfied"
@chanlito_6 жыл бұрын
click baited
5 жыл бұрын
I found a very useful function of async/await Let's think of this scenario: I've got a "route" treatment in express that calls an asynchronous function: The only way I found to deal with that asynchronously was passing "res" to the function in order to it to respond to the client. I HATED THIS. So I need to promisify the async function so it's the "route function" the one who responds and I don't have to pass "res" to other level. (I know I'm not quite clear so I give an example: app.get("/save", (req,res) => { saveFile(req.fileName, req.data, res) }) function saveFile(fileName, data, res) { fs.writeFile(fileName, JSON.stringify(data), (err) =>{ if(err) res.send({saved: false, err: err}); // .status(404) res.status(200).send({saved: true}); }); } I hope this'll serve for you to see my point) Once again: thanks MPJ for your great job
@Misantronic6 жыл бұрын
after working productively with async await for about a year now, I see absolutely no reason to use Promises ever again. the only case is, when a function needs to be async and has to `return new Promise(resolve => ...)`
@JlNGLEZ6 жыл бұрын
line 5 - @17:50 - where's the processUser function ay!
@MikeMcLin6 жыл бұрын
It’s killing me that we are staring at this code snippet that has a glaring bug in it. "processFn" is not a function.
@Luxcium5 жыл бұрын
What makes JS so amazing is its capacity to be used in so many different ways thanks for sharing with us this view on JS
@MiChAeLoKGB6 жыл бұрын
The only reason I find aysnc / await useless is, that my JS has to run in browser (no fancy shmancy node.js) and its not supported in IE 11 which I still have to support. At least I got my boss to agree with me and drop support for IE 10 last week, so now I just have to wait few years for IE to disappear completely and be able to use somewhat new JS stuff. Other than that, this video probably sold the saync / await even more :D
@MiChAeLoKGB6 жыл бұрын
Id like to, but I can't. The company I work at, well its basically only the boss, just avoids any new stuff, like SASS or Babel or any CI/CD etc. If he wont give in, I will probably just leave tho. I did get in some new things, but it was usually by me just doing it and telling him when it was one :D Thats how I partially got in SASS at least for our front-ends...
@MiChAeLoKGB6 жыл бұрын
Yeah, that's what I did with SASS. I really have to push in some sort of CI/CD, like buddy.works or even gitlab hooks, so the babel and SASS gets compiled automagically. The issue is, my boss likes to work on live versions on the server. If I had any real time to do so, I probably would, but he is refusing to hire somebody, because he wants us to have more work than we can comfortably handle (so you end up switching even 4 projects a day), because it keeps us on our toes :D But as I said, I will try to push in buddy or something similar and it that wont happen, Ill just probably go. I want to learn and use new stuff, not only hear/read about it, without even trying it that much, because I just don't want to see any code when I get home... I guess I'll just stop talking now...
@dungandonuts6 жыл бұрын
For the processAllUsers example, you basically implemented reduce with recursion! I can see why you might want keep the waitForEach function separate since it's useful in a variety of cases too though. users.reduce( (acc, curr) => acc.then(() => processUser(curr.id)), Promise.resolve() ))
@Psinite6 жыл бұрын
Your example serialises all async processUsers. It would be better to reduce it into an array of promises that you then cast promise.all on.
@dungandonuts6 жыл бұрын
psi I think the example in the video runs them in sequence but that's a good point, you could map processUser over the users and then run Promise.all on the resulting array to run in parallel. Some promise libraries also have a "sequence" function so you could also use that if for some reason they needed to be in sequence.
@FlyingOctopusKite6 жыл бұрын
😊 good video. Was a fun thread and challenge. One key thing is that you should understand promises to use async / await. One thing I like about promises is you don’t have to make up your own implementation for error handling, promise catches can be tricky but you can do useful things with them in a system. Not having error handling is like making an http call and always expecting a response.
@kmikce696 жыл бұрын
Hi! Your videos are awesome!! I have been working 2 years with Javascript but my brain is going to explode with the code shown in the min ~10:30 when you use the recursion stuff to make this more clever... Could you please that code to me (for dummies it would be nice haha) ?? I am trying to debug it in my mind but I get stucked. Thank you even if you could waste some minutes to it or not, I love your videos anyway :)
@kmikce696 жыл бұрын
Specially the [head, ... tail] and their flow into the recursion...!!
@funfunfunction6 жыл бұрын
The code is based on a recursive function. If you find recursion hard there is a recursion video explaining the basics. It's weird as hell if you're not used to it but pretty normal to functional programmers. The ... is unrelated to recursion and is a separate thing - google array spread.
@ColinRichardson6 жыл бұрын
Await functions are promises, Promises can be "awaited" for.. Just use which ever makes the most sense. Only time I think it looks a little ugly is if you want to do something in parallel. const [value1, value2] = await Promise.all(asyncFunction1(someArgs1), asyncFunction2(someArgs2)); And to be honest, even then, it's very acceptable, especially if there are variables above the promise all that you need to re-use again.
@microcontrolledbot6 жыл бұрын
async / await is one of the best features ever added to node. I think mysql and request packages needs to get their ass in gear and return promises when no call back function is specified so a wrapper is not needed.
@argyleink6 жыл бұрын
I feel like Promise.all() was missing, especially in the processAllUsers example
@retiar21116 жыл бұрын
I just thought about error handling. Taking your word for nexts week episode :D
@pepkin886 жыл бұрын
I would prefer Array#reduce rather than an additional function for that. users.reduce((promise, user) => { return promise.then(() => processUser(user.id)) }, Promise.resolve()) But I would prefer yield/await syntax instead of pure promises (and LiveScript instead of JS).
@MrGrokku6 жыл бұрын
I think there is an inverse correlation between cleverness and readability. If you had a big application in production written as the first promise example it would be hell to understand, and new developers, especially the ones with less experience, would have to be handheld for a long time before they could participate in production. Ternary operators help making that worse. In my opinion, if you have to chain function calls on either side you should just use a conditional block. On the promise examples, I think they miss the ability to return a value from inside a promise's then instead of creating and nesting another promise inside it, and also the ability to simply pass the function that will handle the next step, defined elsewhere, which would be a little more functional. Personally, I don't like "async / await", but mostly because then you have to use "try / catch" blocks which end up cluttering the code even more, but that is not an informed opinion, I haven't worked with it enough to have an opinion on the topic.
@jondreauxlaing6 жыл бұрын
It's too bad that Promises aren't actually monads, because there could have been a much more generic async/await solution like do notation in Haskell. This sort of thing seems to plague the JS community where overly specific, yet easy solutions dominate over generic, and slightly harder solutions. That was kind of the issue that created Promises in the first place. If the JS community at large was cool with main-stream monads, then this issue probably could have been solved a while ago. Instead now we have new keywords for dealing with mapping and binding not-quite-Monads, which only work in those scenarios. I'll admit, I've never gotten a chance to use async/await since it's too bleeding edge for most of the professional projects I've worked on, and I'm not excited enough about it to use it in personal projects, but I do wonder how error handling is done. Like what happens when you await on a call that fails? Do you just get an exception thrown? That doesn't seem to solve the problem imho.
@nickngqs6 жыл бұрын
yeah you can throw an exception by your self and use try catch in the await. tbh i think handling errors in async/await is more elegant than full on promises?
@AngusMcIntyre6 жыл бұрын
Can you describe what it is about promises that are not completely monadic? I'm currently wrestling with the pattern and am curious!
@jason_v123456 жыл бұрын
If a language feature obviates the need to "bother" learning something more complex, doesn't that fit the very definition of "useful"?
@funfunfunction6 жыл бұрын
It doesn't doesn't do that, though. If you don't understand promises, you should really not be using async / await yet,
@tahoemph6 жыл бұрын
Like others I find the burger example wanting. But for another reason. It is racy. You ask the server if it has burgers and then you do some things asynchronous to whatever the server is doing and then you assume if earlier the server has burgers it still does. This is wrong no matter how you structure the code. I think most people would read user processing better as a loop and as soon as you start attempting to emit messages in a manner separate from the messages and/or gather up a list of success/failures to be displayed the waitForEach version starts involving more complexity in the shape of closures. Maybe old lispies would like this version better but then you should rename head/tail as car/cdr then. I think you should really ask a different question from "Is async / await useless". Instead ask "when is promises best and when is async / await best". Near the end you mentioned that async / await shines when you are gather up stuff. So a sequence of code that looks like "get a, based on a get b, based on b get c, ...'. Concretely maybe 'get user, get users profile, get users avatar, etc., return the enhanced user object'. One places where promises shine is when you wan to fire off a bunch of activity and then block on all or part of it being done. I think in order to make good use of async / await you are going to have to understand how promises work and it would be criminal if you use promises not to understand when async / await makes your code look cleaner.
@daviddesmarais-michaud43456 жыл бұрын
Async/await has performance benefits. I used to think that the abstraction would have some overhead cost on top of promises, but in actuality, what it allows for is to not have promises chained in memory and therefore allows for more effective garbage collection and more stable performance. async/await is great. But people should understand callbacks/promises/generators before using them or else they won't ever understand the underlying nature of javascript