The question: Is there, (and if yes, to what extent) a computational overhead in the engine runtime to declare a function as async
and to eventually await
as compared to a regular function's return statement ?
async function foo() { var x = await bar(); // <--- bar() is non-blocking so await to get the return value return x; // the return value is wrapped in a Promise because of async }
Versus
function foo() { var x = bar(); // <--- bar() is blocking inside its body so we get the return value return new Promise(resolve => { resolve(x); }); // return a Promise manually }
Context:
Due to the asynchronous direction taken by Javascript (and i.e. Nodejs), why did they not consider every function to be asynchronous (as per async
keyword) by default ?
This way, people could just decide to treat any function call as a Promise
and play the asynchronous game, or just await
what is necessary.
I suppose that await
-ing within a function body creates the overhead of stacking the local function's scope whereas the normal event loop proceeds when the function returns and does not have to push the inner function scope to the stack ?
This comes down to a bonus question : in a complex hyerarchy of classes that (somewhere deep) requires one synchronous IO operation (see note) that would ideally be await
'ed. It is only possible if that method is marked as async
. Which in turn required the calling function to be async
to be able to await
it again and so forth. Thus, everything marked async
and await
when needed... How to deal with such a scenario ?
Note: Please do not argue about the necessity of not-doing any sync operations as this is not the point.
Note 2: This question is not about what is await
or async
nor when it executes. This question is about performance and the internals of the language (even though multiple implementations exist, there may be a inherent semantic overhead to the concept).
An async function is a function declared with the async keyword, and the await keyword is permitted within it. The async and await keywords enable asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need to explicitly configure promise chains.
with async / await , you write less code and your code will be more maintainable than using the previous asynchronous programming methods such as using plain tasks. async / await is the newer replacement to BackgroundWorker , which has been used on windows forms desktop applications.
C# Language Async-Await Async/await will only improve performance if it allows the machine to do additional work.
JavaScript is a single-threaded, non-blocking, asynchronous, concurrent programming language with lots of flexibility.
An async function has inherent overhead compared to a synchronous function. It's certainly possible to make everything async but you would likely run into performance issues pretty quickly.
A function returns a value.
An async
function creates a Promise object to return from the function. The Promise object is setup to maintain the state of the asynchronous task and handle errors or subsequent chained calls. The promise will be resolved or rejected after the next tick of the event loop. (That's a bit brief, read the the spec if you want detail) This has both a memory and processing overhead compared to a simple function call and return value.
Quantifying the overhead is a bit useless though, as most async functions are async due to them having to wait for an external Node.js thread to complete some work, normally doing slow IO. The overhead in setting up the Promise is pretty minimal compared to the overall time of the operation, especially if the alternative is to block the main JS thread.
Synchronous code on the other hand, runs immediately in the main JS thread. The crossover area is scheduling synchronous code, either for timing or for "throttling" the use of the main JS thread onto the next tick so GC and other async tasks get a chance to run.
If you're in a tight loop parsing a string char by char, you probably don't want to be creating a promise and waiting for it to resolve on each iteration as the memory and time requirements to complete the process will explode quickly.
On the other hand, if all your app does is query a database and dump the results to a koa http response then your likely doing most things in an async promise (although underneath there will still be a lot of synchronous functions making that happen).
A benchmark of a contrived example, the difference between a sync return and various async methods of resolving the same synchronous operation.
const Benchmark = require('benchmark') const Bluebird = require('bluebird') let a = 3 const asyncFn = async function asyncFn(){ a = 3 return a+2 } const cb = function(cb){ cb(null, true) } let suite = new Benchmark.Suite() suite .add('fn', function() { a = 3 return a+2 }) .add('cb', { defer: true, fn: function(deferred) { process.nextTick(()=> deferred.resolve(a+2)) } }) .add('async', { defer: true, fn: async function(deferred) { let res = await asyncFn() deferred.resolve(res) } }) .add('promise', { defer: true, fn: function(deferred) { a = 3 return Promise.resolve(a+2).then(res => deferred.resolve(res)) } }) .add('bluebird', { defer: true, fn: function(deferred) { a = 3 return Bluebird.resolve(a+2).then(res => deferred.resolve(res)) } }) // add listeners .on('cycle', event => console.log("%s", event.target)) .on('complete', function(){ console.log('Fastest is ' + this.filter('fastest').map('name')) }) .on('error', error => console.error(error)) .run({ 'async': true })
Run
→ node promise_resolve.js fn x 138,794,227 ops/sec ±1.10% (82 runs sampled) cb x 3,973,527 ops/sec ±0.82% (79 runs sampled) async x 2,263,856 ops/sec ±1.16% (79 runs sampled) promise x 2,583,417 ops/sec ±1.09% (81 runs sampled) bluebird x 3,633,338 ops/sec ±1.40% (76 runs sampled) Fastest is fn
Also check bluebirds benchmarks if you want a more detailed comparison of the performance/overhead of the various promise and callback implementations.
file time(ms) memory(MB) callbacks-baseline.js 154 33.87 callbacks-suguru03-neo-async-waterfall.js 227 46.11 promises-bluebird-generator.js 282 41.63 promises-bluebird.js 363 51.83 promises-cujojs-when.js 497 63.98 promises-then-promise.js 534 71.50 promises-tildeio-rsvp.js 546 83.33 promises-lvivski-davy.js 556 92.21 promises-ecmascript6-native.js 632 98.77 generators-tj-co.js 648 82.54 promises-ecmascript6-asyncawait.js 725 123.58 callbacks-caolan-async-waterfall.js 749 109.32
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With