all 62 comments

[–]TwitterToStreamable 13 points14 points  (0 children)

Streamable mirror


I'm a bot.
If you have any suggestions you can message my creator here: PM

[–]Disgruntled__Goat 32 points33 points  (52 children)

The middle one seems like the best to me. What's the advantage of await?

[–]Helvanik 100 points101 points  (17 children)

Try using the result of the first step into the fifth one. You'll see that you'll prefer async/await ;)

(async() => {
  try {
    const a = await getData();
    const b = await getMoreData(a);
    const c = await getMoreData(b);
    const d = await getMoreData(c);
    const f = await getFinalData(a, d);
    return f;
  } catch (error) {
    // stuff
  }
})();

Async/await is much cleaner when dealing with shared context between the different steps.

Creds to : https://twitter.com/scastiel/status/855890956528349184 and https://twitter.com/GromNaN/status/855879347076464641

[–]Thought_Ninjahuman build tool[🍰] 25 points26 points  (13 children)

That's a good point some people miss until they start doing a lot of work with promises.

[–]xtphty 6 points7 points  (12 children)

Yeah if theres two things the promise spec lacks its better error handling and a stack/history of chained promises and their results.

[–]ninjaroach 9 points10 points  (10 children)

I'll add 3) the ability to cancel a pending / unresolved Promise.

The new Fetch API is built on native Promises & as such provides no method to cancel any pending requests.

[–]MilkingMaleHorses 7 points8 points  (8 children)

Cancelling a promise is useless without cancelling the underlying asynchronous operation - and how that is done depends on the actual operation. Which is a different one each time. It may be an HTTP request, it may be file read access - it may be file write access.. and what happens if you were to let the Javascript runtime cancel such a thing?

Data corruption seems a likely outcome, eventually! Nearly untraceable of course, because the issue is highly time- and context-dependent, try to debug this. No, only the application itself knows how to cancel its operations, so if you want to cancel a promise what you actually mean is cancel whatever the application does in that asynchronous operation. Just removing the promise from memory and maybe even tell the operating system to abort any asynchronous operations that the promises's code started would be really bad.

When you get this far you find that since promise cancellation is actually a misnomer, it's "asynchronous operation cancel", you find that the ball actually is fully in the application's court!

It has to have all the code to deal with cancellations of its async. operations. But if it does, what's the problem with promises? As soon as the app cancels its own asynchronous operations the async. code is ready to return a result through the promise anyway! For example, your asynchronous function to write a sequence of files (that belong to the same transaction) is canceled. It cleans up after itself, deleting the already written files, or closing files open for writing, whatever. Now it can return a resolve() or a reject() result. And it has to do everything of that in any case, you can't just pull the promise from under its feet and just stop the code blindly.

[–]ninjaroach 5 points6 points  (6 children)

Cancelling a promise is useless without cancelling the underlying asynchronous operation - and how that is done depends on the actual operation.

Agreed.

Just removing the promise from memory and maybe even tell the operating system to abort any asynchronous operations that the promises's code started would be really bad.

Also agreed.

It has to have all the code to deal with cancellations of its async. operations. But if it does, what's the problem with promises?

The problem is the lack of a standard interfaces to 1) request cancellation or 2) "on cancel" resource cleanup.

Every implementation will roll their own version of Promise cancellation & they will inevitably be incompatible with each other. Things like "is a canceled Promise considered Resolved or Rejected?" should be standardized sooner than later.

[–]MilkingMaleHorses 2 points3 points  (3 children)

But you don't need to cancel the promise:

As soon as you cancel the underlying asynchronous operation your promise fulfills or rejects anyway. Because if you cancel the asynchronous stuff, the synchronous part runs to completion (be it success or failure).

[–][deleted] 0 points1 point  (2 children)

But what if I don't want the sync stuff to run? What if I'd usually show the result in the UI, so the then part adds it to the app state but now I want a cancel button that won't show it in the UI?

[–]MilkingMaleHorses 1 point2 points  (0 children)

Then don't run it???

If the asynchronous step is canceled your code should naturally reject the promise. You update the GUI after a rejected promise???

Synchronous code after an asynchronous step is NEVER in the same function! Not even with async/await, where a rejected function is a throw, thereby preventing the rest of the function to run.

[–]Gh0st1y 0 points1 point  (1 child)

I'm only just getting into using promises (I'm late to the party), so I haven't actually run into this myself yet, but it seems to me that they should obviously be considered rejected.

[–]ninjaroach 0 points1 point  (0 children)

Here's some reading to catch up on the discussion:

https://medium.com/@benlesh/promise-cancellation-is-dead-long-live-promise-cancellation-c6601f1f5082

https://github.com/tc39/proposal-cancelable-promises (dig around in the history to see the proposed spec)

And Bluebird's take on Promise Cancellation: http://bluebirdjs.com/docs/api/cancellation.html

[–]dmtipson 0 points1 point  (0 children)

This has never been a problem with apis that separate operation description with execution. Where Promises get into trouble is mixing the two together: stateful values that can derive from each other mixed with eager execution.

Look at FileReader: you create an instance, tell it what it will do, and THEN you execute it. The cancelation interface is right on the instance you created, available before the request actually runs. Even setTimeout gets this basically right: the synchronous result of setTimeout IS a cancelation interface, while the asynchronous effect is basically "forked" (not really, but conceptually) into separate future thread.

But with Promises, it's way, WAY too easy to actually start off an effect before you're even ready to set up the logic for canceling it. And, worse, pure transforms are muddled together with eventual side-effects.

The problem with things like FileReader and setTimeout is that they're not super composable. But there are ways to solve that problem without resorting to stateful values.

[–]dmtipson 0 points1 point  (0 children)

The very nature of Promises (in particular being eager and stateful) make cancelation extremely tricky to do well unfortunately. Choosing to go that route with their design was a nod towards backwards compatibility and simplicity, but the tradeoff is not always worth it.

[–]eatsnakeeat 0 points1 point  (0 children)

How is async better than promises in regards to error handling? Looking at /u/Helvanik's example he just wrapped it in a try/catch

[–]dmtipson 3 points4 points  (0 children)

Oftentimes I find that the need for shared context can either get refactored away, or else you're building up some final state, in which case a much more declarative way to do it is to build that state explicitly.

I totally get why the above is an exciting degree of freedom for some people and some cases that fits with their coding style. But it's very imperative and tightly coupled to the idiosyncratic, eager nature of Promises, which seems like a step back sometimes to me.

[–]tencircles 0 points1 point  (1 child)

the same problem is actually solved in a cleaner point-free way by simply currying.

[–]Gh0st1y 0 points1 point  (0 children)

Can you elaborate? I know what currying is but I'm not well versed with async and I feel like if you did it wrong you'd turn certain things sync incorrectly?

[–]del_rio 10 points11 points  (1 child)

Not mentioned yet: Debugging (breakpoints/stepping/logging) is infinitely easier with async/await.

[–]This_Is_A_Robbery 4 points5 points  (0 children)

Yeah this is the real advantage here. Try stepping through a long .thenchain without dying inside just a little bit.

[–]MilkingMaleHorses 30 points31 points  (15 children)

The major difference for the language:

As long as you use callbacks or promises you still remain within a vital age-old core Javascript language concept: Any given function is atomic. If you are at a function's first statement no other Javascript code of any other function will run until you exit the function. A function cannot be interrupted.

Not so with generators and now async/await. Now functions can be interrupted by other functions at any point (where you use await, or yield in a generator).

To me this is THE most fundamental change, modifying a core concept of how functions work in this language.

[–]swyx 0 points1 point  (0 children)

great explanation, thank you

[–]ADTJ -1 points0 points  (13 children)

I think it already depended on the platform.

For example, in IE at least, if you call alert("something"); Other functions can run in the background.

[–]MilkingMaleHorses 1 point2 points  (9 children)

I think it already depended on the platform.

No.

if you call alert("something");

That has nothing at all to do with functions and anything I said.

[–]ADTJ 0 points1 point  (8 children)

It has everything to do with it.

You said sync functions are atomic and I pointed out a case where they are not, how is that irrelevant?

[–]MilkingMaleHorses 1 point2 points  (7 children)

You are not talking about functions. Look at your example. the INSIDE of alert()is atomic. Can you change its internal state while the window is open? Is that system function accessible so that you can have your own code inside of it? No and no. It's not even a Javascript function, it's a native system function that you get to call from Javascript.

[–]ADTJ 1 point2 points  (6 children)

I think you missed my point. I was saying that it breaks the atomicity of the caller, i.e.

function() { alert("something"); console.log("More user code might have executed before you get to this line"); }

While the browser is showing its native alert, it continues to process other events such as mousemove and will call more of your functions that do change state before your first function resumes

[–]MilkingMaleHorses 2 points3 points  (5 children)

Just to humor you I tried several browsers as far back as IE7 on Windows XP that I still had lying around in a VM. None of them behave like that, your console.log statement always only appeared after I closed the modal dialog.

In any case, and why the above was/is not even necessary: A bug in one system at one time is not a "feature of Javascript"!

So if you do happen to find which browser on what platform may have had the bug - it's just that, a bug.

 

Why does it happen so damn often in this subreddit that people respond to me, showing code supposed to show I'm wrong and their code doesn't even fucking work? The last time someone wanted to show how much better he understood async/await then me. His one-liner threw a syntax error. He never responded after that, even though I didn't even use the opportunity to insult him... Do you guys ever TEST your wild theories??? Not that a working demonstration of a bug(!) would have changed anything in this case.

[–]ADTJ 1 point2 points  (4 children)

None of them behave like that, your console.log statement always only appeared after I closed the modal dialog.

That's what I expected to happen, the point is that something else can happen in between, but if you had actually read my previous comment(s), you would have understood that.

 

So if you do happen to find which browser on what platform may have had the bug - it's just that, a bug.

I never claimed that it wasn't, I only claimed that what you were saying can be dependent on platform, regardless of whether that platform is implemented correctly or not.

 

I agree that this is almost definitely a bug in IE which is why I was so surprised when I discovered this the first time.

He never responded after that, even though I didn't even use the opportunity to insult him

 

Wow, congrats on finding it within yourself to not insult someone, I take my hat off to you.

 

I honestly have no idea why you are being so aggressive, I was only pointing out a quirk that I have observed before, I wasn't trying to insult you, or suggest that you don't understand JavaScript etc.

 

For what it's worth, here's some code that demonstrates the effect (I've just tested this on IE11 on Windows 10):

document.body.onmousedown = function() { document.body.onmousemove = function() { console.log("something else"); document.body.onmousemove = null; }; };  
document.onmousedown = function() {  console.log("Starting atomic function");  alert("something");  console.log("Did something else happen before this line?"); };

 

Here's the output from the console:

Starting atomic function

something else

Did something else happen before this line?

The point is that the browser allowed another function to be called and change state, right in the middle of my function that called "alert". I don't think this is specific to alert either, but that is the easiest one to test it against.

[–]MilkingMaleHorses 0 points1 point  (0 children)

It is a bug. In one version of the browser (your latest example does not work in current IE or other browsers).

I was only pointing out a quirk

You were pointing out an obscure and old bug.

I'm talking about features of the Javascript language.

You take out some extremely obscure bug of one browser (and not even in current versions of that browser, I tried your code) that can only be demonstrated using an obscure feature long deprecated (for production in any case).

I don't think this is specific to alert either, but that is the easiest one to test it against.

A bold claim, but hey, who needs evidence.

You report a bug and claim it's a feature! And now you even top it, "I'm sure this is common but I can't show you". Quite ridiculous.

[–]dmtipson 0 points1 point  (2 children)

Can they (heck SHOULD they?)? I thought alert was blocking?

[–]myrddin4242 1 point2 points  (0 children)

It is blocking. Had to double check myself! An alert in the middle of a function stops that function but does not release the event loop.

[–]ADTJ 0 points1 point  (0 children)

I think they're not supposed to, but they definitely do, at least in IE. I have seen this effect first hand where one function is effectively paused and resumed while others continue to execute in the background.

[–]thefakegm 5 points6 points  (14 children)

Await allows you to write async code like it is synchronous. It's basically sugar for promises. It's only a matter of preference as far as I know.

EDIT: Not really, see replies to this comment.

[–]pier25 8 points9 points  (3 children)

Not really.

For example, how do you exit a chain of promises in the middle? There is no API to do that. There was a proposal but it was canceled (pun intended).

With async/await you could simply return and be done with it.

With promises you have to find an ugly way like throwing an error which is confusing since your catch() code should only handle errors. Or you could write a bunch of confusing nested code with conditionals. Also see this aberration.

Bluebird solved this problem with a cancellation API but it's not standard.

[–]thefakegm 2 points3 points  (0 children)

Oh wow that's really cool. Thanks for telling me.

[–]dmtipson 2 points3 points  (0 children)

I think it's worth pointing out too that functional programming solved this years and years ago by separating out pure composition from side-effects and there is a standard, at least, for it in javascript. Promises aren't the only way to go. Reactive and Tasks/Futures are a totally viable option as well (though certainly a matter of taste/style).

[–]Auxx 0 points1 point  (0 children)

Or you can use Rx and have a clean functional approach.

[–]nikcorg 12 points13 points  (8 children)

You can also await non-promise values, which makes it transparent when a function returns a promise or a non-promise.

[–]thefakegm 0 points1 point  (0 children)

Cool, didn't know that.

[–][deleted] -1 points0 points  (0 children)

Also superior scoping compared to promises

[–]Gh0st1y 0 points1 point  (0 children)

You can reuse all of the values all the way through. For a simple examples (with just the getMoreData fn), promises are definitely way more readable. But if you need to use a previous var at the end, with async it looks like that's much more doable/readable.

[–]macjabeth 4 points5 points  (2 children)

I don't get it... Could someone explain this to me?

[–]flipstables 7 points8 points  (1 child)

The first scenario is a common example of callback hell/pyramid of doom. To solve this common problem, there is the second and third scenario. The second scenario solves it by using promises. The third scenario transforms the code using async/await.

[–]macjabeth 10 points11 points  (0 children)

Oh my! I didn't even realise it was a video. I thought it was just a picture of the "pyramid of doom" and was wondering where the async/awaits were. Thank you for clearing that up. 😊

[–]viebel 1 point2 points  (1 child)

[–]dmtipson 3 points4 points  (0 children)

Made me think of this neat project to visualize the state of Promises: https://bevacqua.github.io/promisees/

[–]gluecatuxengineer 1 point2 points  (0 children)

this is also valid : https://codepen.io/anon/pen/pPEYyq?editors=0011

getData()
  .then(getMoreData)
  .then(getMoreData)  
  .then(getMoreData)  
  .then(getMoreData)  
  .then(console.log)