You Don’t Know JS: Async & Performance
The problem most developers new to JS seem to have is that later doesn’t happen strictly and immediately after now. In other words, tasks that cannot complete now are, by definition, going to complete asynchronously, and thus we will not have blocking behavior as you might intuitively expect or want.
So, in other words, your program is generally broken up into lots of small chunks, which happen one after the other in the event loop queue. And technically, other events not related directly to your program can be interleaved within the queue as well.
It’s very common to conflate the terms “async” and “parallel,” but they are actually quite different. Remember, async is about the gap between now and later. But parallel is about things being able to occur simultaneously.
Are you catching the notion here that our sequential, blocking brain planning behaviors just don’t map well onto callback-oriented async code? That’s the first major deficiency of callbacks: they express asynchrony in code in ways our brains have to fight just to keep in sync with (pun intended!).
We call this inversion of control, when you take part of your program and give over control of its execution to another third party. There’s an unspoken contract that exists between your code and the third-party utility — a set of things you expect to be maintained.
Another common callback pattern is called “error-first style” (sometimes called “Node style,” as it’s also the convention used across nearly all Node.js APIs), where the first argument of a single callback is reserved for an error object (if any).
Hard-to-reason-about code is bad code that leads to bad bugs.
As such, it was decided that the way to recognize a Promise (or something that behaves like a Promise) would be to define something called a thenable as any object or function which has a then(..) method on it.
Jeff Atwood noted years ago that programming languages are often set up in such a way that, by default, developers fall into the “pit of despair” — where accidents are punished — and that you have to try harder to get it right. He implored us to instead create a “pit of success,” where by default you fall into expected (successful) action, and thus would have to try hard to fail.
And if there is no return in your generator — return is certainly not any more required in generators than in regular functions — there’s always an assumed/implicit return; (aka return undefined;), which serves the purpose of default answering the question posed by the final it.next(7) call.
“There is nothing more permanent than a temporary hack.”
I’d even go so far as to say this: no amount of time spent optimizing critical paths is wasted, no matter how little is saved; but no amount of optimization on noncritical paths is justified, no matter how much is saved