As cowboy says down in the comments here, we all want to “write [non-blocking JavaScript] asynchronous code in a style similar to this:
try { var foo = getSomething(); // async call that would normally block var bar = doSomething(foo); console.log(bar); } catch (error) { console.error(error); }
“
So people have come up solutions to this problem like
- callback libraries (eg async)
- promises
- event patterns
- streamline
- domains and
- generators.
But none of these lead to code as simple and easy to understand as the sync-style code above.
So why isn’t possible for javascript compilers/interpreters to just NOT block on the statements we currently know as “blocking”? So why isn’t possible for javascript compilers/interpreters to handle the sync syntax above AS IF we’d written it in an async style?”
For example, upon processing getSomething()
above, the compiler/interpreter could just say “this statement is a call to [file system/network resource/…], so I’ll make a note to listen to responses from that call and in the meantime get on with whatever’s in my event loop”. When the call returns, execution can proceed to doSomething()
.
You would still maintain all of the basic features of popular JavaScript runtime environments
- single threaded
- event loop
- blocking operations (I/O, network, wait timers) handled “asynchronously”
This would be simply a tweak to the syntax, that would allow the interpreter to pause execution on any given bit of code whenever IT DETECTS an async operation, and instead of needing callbacks, code just continues from the line after the async call when the call returns.
As Jeremy says
there is nothing in the JavaScript runtime that will preemptively pause the execution of a given task, permit some other code to execute for a while, and then resume the original task
Why not? (As in, “why couldn’t there be?”… I’m not interested in a history lesson)
Why does a developer have to care about whether a statement is blocking or not? Computers are for automating stuff that humans are bad at (eg writing non-blocking code).
You could perhaps implement it with
- a statement like
(a bit like"use noblock"
;"use strict";
) to turn this “mode” on for a whole page of code. EDIT:"use noblock"
; was a bad choice, and misled some answerers that I was trying to change the nature of common JavaScript runtimes altogether. Something like'use syncsyntax';
might better describe it. - some kind of
parallel(fn, fn, ...);
statement allowing you to run things in parallel while in"use syncsyntax"
; mode – eg to allow multiple async activities to be kicked off at once - EDIT: a simple sync-style syntax
wait()
, which would be used instead ofsetTimeout()
in"use syncsyntax"
; mode
EDIT:
As an example, instead of writing (standard callback version)
function fnInsertDB(myString, fnNextTask) { fnDAL('insert into tbl (field) values (' + myString + ');', function(recordID) { fnNextTask(recordID); }); } fnInsertDB('stuff', fnDeleteDB);
You could write
'use syncsyntax'; function fnInsertDB(myString) { return fnDAL('insert into tbl (field) values (' + myString ');'); // returns recordID } var recordID = fnInsertDB('stuff'); fnDeleteDB(recordID);
The syncsyntax
version would process exactly the same way as the standard version, but it’s much easier to understand what the programmer intended (as long as you understand that syncsyntax
pauses execution on this code as discussed).
Advertisement
Answer
Why not? No reason, it just hadn’t been done.
And here in 2017, it has been done in ES2017: async
functions can use await
to wait, non-blocking, for the result of a promise. You can write your code like this if getSomething
returns a promise (note the await
) and if this is inside an async
function:
try { var foo = await getSomething(); var bar = doSomething(foo); console.log(bar); } catch (error) { console.error(error); }
(I’ve assumed there that you only intended getSomething
to be asynchronous, but they both could be.)
Live Example (requires up-to-date browser like recent Chrome):
function getSomething() { return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() < 0.5) { reject(new Error("failed")); } else { resolve(Math.floor(Math.random() * 100)); } }, 200); }); } function doSomething(x) { return x * 2; } (async () => { try { var foo = await getSomething(); console.log("foo:", foo); var bar = doSomething(foo); console.log("bar:", bar); } catch (error) { console.error(error); } })();
The first promise fails half the time, so click Run repeatedly to see both failure and success.
You’ve tagged your question with NodeJS. If you wrap the Node API in promises (for instance, with promisify), you can write nice straight-forward synchronous-looking code that runs asynchronously.