Hacker News new | ask | show | jobs
by 0xff00ffee 2239 days ago
I always skip to the async part of JS text because explaining it is a bit like handing a shotgun to a five year old. I think the folks over at risingstack have the best explanations, IMHO.

I'm surprised they attempted timeouts in promises with so little text, that's actually pretty dangerous pattern because it glosses over the complexity in actually stopping an in-flight promise. It is VERY easy to end up with hundreds of thousands of unresolved promises with their pattern. Dangerous!

There is a great repo on this issue.... aand I can't for the life of me find it. Basically there's a github project that uses generators and passes an atom down through the call stack to ensure everything below the race promise is aware that it is being halted. And even that doesn't handle all the nuances of this pattern.

And if anyone knows what github repo I'm talking about, I'll give you ... 50 DKP?

2 comments

I used to use more "Promises" but now I mostly code with plain callbacks if possible. It is just simpler, less thinking needed. I spend that thinking-budget on other things.

One thing that is tricky with async is that nobody tells you if the async function never does what it should. I wish they would add some more built-in support for that in the next JS version.

Promises hold promise (pun intended) but they are a bit too complicated for my brain.

I also find promises problematic. I do like async/await though, except for the fact that it relies on promises.

I've been toying with the idea of a promise free async/await implementation lately. I'm interested in feedback anyone would have.

https://github.com/bessiambre/casync

Very interesting. I just don't have time to try it out right now.

Perhaps if you create and publish a simple comparison solving a given problem with Promises vs. CaSync it would show that CaSync takes much less code?

That could convince more people to take the time to try it out.

I'm not sure it's much less code. Especially that async/await is integrated into the language now. It's less state, less corner cases, more encapsulation and better performance.

I'm thinking of trying (if I have time) to build a babel transpiler plug-in so you could use it with keywords like async/await instead of explicitly wrapping generator functions. I think the syntax would end up very simple.

Callbacks are a bit too much of an eye sore imo.

>One thing that is tricky with async is that nobody tells you if the async function never does what it should

Could you clarify this?

In sync programming if your function does the wrong thing you typically get an error thrown or you get a result which you check for its correctness.

But when you call an async-function that is supposed to do something like say write to a file or database maybe there is an error which makes it never do its job, and never call the callback you gave it. But the rest of your program just hums along happily. There is no error. The error does not "happen" but the error is that "something did NOT happen". And when something does not happen you don't get an error or notification saying that something did not happen.

You run your program but expected results do not show up in database. But you don't know why, because the problem is that some of your async functions did NOT call something they should have called.

It's hard to detect that something does not happen.

Some kind of built-in support for callback-timeouts might alleviate this problem.

What are you talking about? if you're async operation fails then your callback should always still be called but just with an error message in the second parameter typically. And what are you doing still giving call backs to async functions? Why are you not using promises?
I think previous poster is talking about a bug where the promise is never resolve nor rejected. So it just hangs out there, unresolved, forever.

I don't think using explicit callbacks helps, though, which has the same potential issue (worse really, since you're probably handling callbacks directly more often, depending on the patterns you use).

Right. Except I was able to experiment with a wrapper-function which set up a timeout on the callback which threw an error if the callback was not called within a set interval of time.

It helped to detect some flaws in my program.

A similar thing could no doubt be done with promises. But I think the best solution would be if there was a built-in facility to detect callbacks that were not called within a required or default time interval.

Eventually I stopped using the timeout wrapper, it didn't help very much, but made the code more complex. More but easier to understand code is often my preferred choice.

Whereas if language itself offers built-in facilities then fine since they are more likely to be bugfree than my own code.

Javascript code doesn't just stop executing for no reason, if the callback needs to stop executing, it should reject the promise or throw an error. Or resolve with an error value. That's like saying "in sync programming, if you don't check for error conditions, your code continues humming along happily just the same".

If there is an error when reading a file or accessing a database, unless the file or database API is horrendously designed, it will reject the promise or throw.

> Javascript code doesn't just stop executing for no reason,

I would say it stops unless there is something keeping it running.

Think about your single-page-web-app. When you click on some widget on it a click-handler triggers and executes that code. But then it stops. When the user doesn't interact with your web-app no JavaScript is typically executing, unless you have set up a repeating polling loop with setInterval().

The scenario you described is a classic one and I really don't think it's as problematic as you may think. I had to reread it because it sounded too trivial. How about just throwing an error when the async function fails to write to the db?

>maybe there is an error which makes it never do its job

You can throw an error regardless of the reason why it failed to do its job. If the write to the db does not success, you can throw an error.

You are supposed to call fs.write() etc. But you do it only under certain logical conditions. Those logical conditions do not arise like you would expect because of logic errors elsewhere in your code.

So your async function never calls fs.write() even though it was your intention that it should. Is that an error? Definitely, the file was never written to, it now has wrong content. But the problem is, as you run your program you do not get any error thrown at you, therefore you don't even know there is a problem. Later on you may, or may not, realize that the content of the file is wrong. And then it's hard to say what caused that error.

What "caused something not to happen" is a difficult question to answer because you can't pinpoint the exact location in time and code where it did not happen. Why? Because it did not happen anywhere, ever :-)

Btw this same issue exhibits in python with threads. If you spawn an async computation in a thread and an uncaught error is raised, the thread disappears silently, leaving you with a callback that’s never called or a future that’s never resolved.
Ah interesting to know it's not only JavaScript.

But I assume in a thread-based language like Java all asynchronicity happens due to threads. Then from the programmers' point of view method-calls always either return a result or throw an error. They don't just silently stop executing

Funny, I now treat everything as a promise: all my functions are async because it keeps the interface simpler: if they don't return a promise, they are just a normal function, no harm, no foul.

I see no reason to use callbacks, unless the NodeJS module dev didn't implement promises. And even then I write a wrapper. I'm glad to see later versions of Node include promisified versions of things like File IO.

> glad to see later versions of Node include promisified versions of things like File IO.

I agree. They are easier to use. But they require more effort to write. Promises are easier to use than create. Maybe they are the direction in which public APIs will go but I do find they take extra effort to write, compared to callbacks

Regarding your second point: This sounds similar to .NET‘s CancellationToken. It works very well there.

What are those nuances in JavaScript which aren’t handled by it?

I think you're going to point out that I'm about to demonstrate non-JavaScript nuances, but here are issues I've encountered:

Let's take a great of example of where promises are used most often: AJAX. If the endpoint isn't RESTful and there is state on the other end, cancelling a promise requires updating the entire module of the new state. There can be multiple reasons for cancelling, and the code needs to be aware of what is happening beyond a simple timeout.

Same thing goes for USB devices (via libusb+ffi) and serial-ports (via serialport). In flight requests can disturb state if cancelled, and it makes the programming very complex to just time-out.

One could argue AJAX and hardware programming are outside the domain of JavaScript, but I wouldn't.

I guess it is more than a "nuance", it is the observance that cancelling an asynchronous operation in general can have unintended consequences if not handled correctly ... beyond memory leaks from unresolved promises.