I have recently started with node.js. It quotes that node.js asynchronous behaviour can be used in three ways events, stream, callback.
Events and stream work on emit,pipe event making it truly async, but how is callback async, as it executes before the return of the function unless process.nextTick() is used.
Events:
event.on('data',function(data){
});
Callback:
function(data,cb){
// do something with "data"
cb();
return;
}
Let's take a closer look at your function that has a callback:
function(data, cb) {
// do something with "data"
cb();
return;
}
You have 2 big errors here:
1) You have no reason to use return when you have a callback function. Return is used only when you have synchronous actions that send a response back right away (they do not have to wait for a callback to be triggered sometime in the future).
2) You don't execute the callback immediately, it doesn't make any sense. You either don't use a callback and use a return statement (when you don't really have an async function) or you execute the callback sometime in the future, when you get the result.
Better example:
function (data, cb) {
// you make an Ajax call and
// you don't know when the callback will be triggered
makeAjaxCall(data, cb);
}
You're correct in that callbacks don't necessarily mean asynchronous I/O.
Events aren't necessarily asynchronous either, in the sense that EventEmitter's .emit works immediately, not on the next tick:
var EventEmitter = require('events').EventEmitter;
var emitter = new EventEmitter();
emitter.on('ev', function(x) {
console.log("received event ", x);
});
console.log("Hello");
emitter.emit('ev', "X");
console.log("World");
Result:
Hello
received event X
World
However, callbacks can be made "async" by:
using process.nextTick(). This doesn't make the actual I/O operations inside the callback async. It just defers execution until the next tick. If some operation blocks, the event loop will also block - on the next tick instead of this tick.
function(data, cb) {
process.nextTick(function() { doSomethingWith(data); cb(); })
});
calling the specified callback function from an event that is known to be triggered by an async I/O operation
function(data, cb) {
emitter.startIO();
emitter.on('someIOEvent', function(e) {
doSomethingWith(data,e);
cb();
});
});
using another callback-based function known to be async. Most functions in node core and node's modules are like this.
function(data, cb) {
otherFunction(data, function(moredata) {
doMoreStuffWith(moredata, data); cb();
});
});
In this case, the function with the callback is not being run asynchronously, however there are still cases where such functions can be used asynchronously:
function foo(data, callback) {
// do something with the data
callback();
}
function bar() {
// this will be called as a callback from foo()
}
setInterval(function() { foo(data, bar) }, 1000);
console.log('this is displayed immediately');
You can see that foo() is scheduled to run every second, and that happens asynchronously; however the existence of the callback allows you to set up extra behavior from outside the foo() function, e.g. you might set up several cases where foo() is called with different callbacks, resulting in different application logic.
Related
I was unsure how node.js was able to realize what functions where async and which were not and how to create a custom async function.
Say I wanted to create a custom asynchronous function. I would be surprised if just because I called my last argument to the async function callback or cb that it would just know its an async function:
function f(arg1, callback){
//do stuff with arg1
arg1.doStuff()
//call callback
callback(null, arg1.result());
}
I tried something like that and it did not work async. How do you tell node.js that f is actually async?
NOTE: this answer was written in 2014, before the existence of async function, and before Promises gaining popularity. While the same principles apply as well, I would recommend reading on Promises before trying to get your head around how they relate to "traditional" callback-driven async functions.
To create a function that calls its callback asynchronously, you have to use some platform-provided async primitive (typically IO-related) on it - timers, reading from the filesystem, making a request etc.
For example, this function takes a callback argument, and calls it 100ms after:
function asyncFn(callback) {
setTimeout(() => {
callback();
}, 100);
}
A possible reason for making a function async when it doesn't need to be, is for API consistency. For example, suppose you have a function that makes a network request, and caches the result for later calls:
var cache = null;
function makeRequest(callback) {
if (!cache) {
makeAjax(result => {
cache = result;
callback(result);
});
} else {
callback(cache);
}
}
The problem is, this function is inconsistent: sometimes it is asynchronous, sometimes it isn't. Suppose you have a consumer like this:
makeRequest(result => doSomethingWithResult(result));
doSomethingElse();
The doSomethingElse function may run before or after the doSomethingWithResult function, depending on whether the result was cached or not. Now, if you use an async primitive on the makeRequest function, such as process.nextTick:
var cache = null;
function makeRequest(callback) {
if(!cache) {
makeAjax(result => {
cache = result;
callback(result);
});
} else {
process.nextTick(() => callback(cache));
}
}
The call is always async, and doSomethingElse always runs before doSomethingWithResult.
Only native functions (with access to the event loop) are asynchronous. You would need to call one of them to get asynchronity for your callback. See What is a simple example of an asynchronous javascript function?.
If you aren't using any, there's hardly a reason to make your function asynchronous.
I would like to clarify - when is it correct to use setImmediate for a node callback.
The examples/articles I've studied argue it is best to use setImmediate to ensure a callback is asynchronous. The most common example is where a value might exist in a "cache" e.g.
const getData = function(id,callback) {
const cacheValue = cache[id];
if (cacheValue) {
// DON'T DO THIS ...
return callback(null,cacheValue);
// DO THIS ...
return setImmediate(callback,null,cacheValue);
}
return queryDB(id,function(err,result){
if (err){
return callback(err);
}
return callback(null,result);
});
};
Here's where it gets confusing. Most node examples of error handing in callbacks never seem to call setImmediate. From my example above:
if (err) {
return callback(err);
}
instead of:
if (err) {
return setImmediate(callback,err);
}
I read some arguments that say that setImmediate is not necessary in this case and indeed can impact performance, why is this? Is this example not the same as the example of accessing the cache.
Is it better to be consistent and always use setImmediate? In which case then, why not do the following:
const getData = function(id,callback) {
const cacheValue = cache[id];
if (cacheValue) {
return setImmediate(callback,null,cacheValue);
}
return queryDB(id,function(err,result){
if (err){
return setImmediate(callback,err);
}
return setImmediate(callback,null,result);
});
};
Quick answer
If you're calling the callback synchronously (before the host function has returned), you should use setImmediate(). If you are calling the callback asynchronously (after the function has returned), you do not need it and can call the callback directly.
Longer Answer
When you have an interface that accepts a callback and that callback is at least sometimes called asynchronously (meaning some indefinite time in the future after your function has returned), then it is a good practice to always call it asynchronously, even if the result is known immediately. This is so that the caller of your function and user of the callback will always see a consistent asynchronous interface.
As you seem aware of, a classic example of this would be with a cached result. If the result is in the cache, then the result is known immediately.
There is no law of Javascript-land that one MUST always call the callback asynchronously. Code may work OK if the callback is sometimes called synchronously and sometimes called asynchronously, but it is more subject to bugs caused by how the callback is used. If the callback is always called asynchronously, then the caller is less likely to accidentally create a bug by how they use the callback.
In the case of:
if (err) {
return callback(err);
}
My guess is that this is already in an asynchronous location. For example:
function someFunction(someUrl, callback) {
request(someURL, function(response, body, err) {
// this is already inside an async response
if (err) {
callback(err);
} else {
callback(body);
}
});
}
I read some arguments that say that setImmediate is not necessary in this case and indeed can impact performance, why is this? Is this example not the same as the example of accessing the cache.
In this case, the if (err) is already in an asynchronous callback part of code so there is no need for an additional setImmediate() there. The host function has already returned and thus calling the callback here without setImmediate() is already asynchronous timing. There is no need for an additional setImmediate().
The only time the setImmediate() is needed is when you are still in the synchronous body of the function and calling the callback there would call it before the host function returns, thus making the callback synchronous instead of asynchronous.
Summary
When is setImmediate required in node callback
So, to summarize. You should use setImmediate(callback) when you in code that is executing synchronously before the host function has returned. You do not need to use setImmediate(callback) when the code you are in is already in an asynchronous callback and the host function has already returned.
FYI, this is one reason to exclusively use promises in your asynchronous programming interfaces (instead of plain callbacks) because promises already handle this for you automatically. They guarantee that a .then() handler on a resolved promise will always be called asynchronously, even if the promise is resolved synchronously.
I am confused on whether I should call done() after a function completes execution or return. I understand that calling done will mean that I have to pass it as a parameter to the function. What instances will one opt for calling return rather than done()?
i.e.
var foo = 2;
it('returns 2 on completion', function(done) {
expect(foo).toEqual(2);
done();
});
or
var foo = 2;
it('returns 2 on completion', function() {
expect(foo).toEqual(2);
return;
})
Whether you use a done() callback or simply return depends on the API you're using. A done() callback is necessary in a variety of asynchronous contexts. Off the top of my head:
Tests, like your example
Express middleware (they call it next() instead of done())
Methods in the async library (they call it callback() instead of done())
In all of those contexts, the done() callback is necessary because they need to do work that can't all finish before the return. For example, tests might include random timeouts, Express middleware might make network requests, etc.
Important note: every function returns. The done() callback is a convention you use on top of that when return isn't enough because it happens too soon.
I am new to nodejs, recently I am not sure how to arrange tasks in order such that they behave correctly.
below are two examples
arr.forEach(function(elem){
//do many many things
});
console.log('done, now take ur pick');
stdin.on('input', function action(){
...
})
how can I set stdin.on fire after arr.forEach.
and question 2 I think if I do
fn1();
fn2();
function fn1(){
//long long code
}
function fn2(){
console.log('fn2 is done!')
}
and runs the above, the execution will be in a thread of
fn1 fn2 fn1 fn1 fn1, right? how to prevent this?
Functions in node.js are either synchronous or asynchronous. Synchronous code runs in a single thread, and behaves exactly like the code you're used to:
fn1();
fn2();
fn3(fn4());
Will run fn1, then fn2, then fn4, then fn3 (passing in the result of fn4 as a parameter).
Asynchronous code fires off some action, and takes as a parameter the function to execute when it's done (which might, in turn, fire off another asynchronous action). You get control back immediately. Example:
doSomeAsyncThing(function () {
console.log("did the async thing");
});
console.log("hello");
executes like this: First, the asynchronous action is fired off, then "hello" is printed. Some time later, the asynchronous action completes, so the callback is called, and "did the async thing" is printed.
If you want to do one asynchronous thing, then do another asynchronous thing after the first one is finished, the standard way to is nest the callbacks:
async1(function() {
async2(function() {
console.log("async1 and async2 finished");
});
});
Although there are better options.
So in your second example, if fn1 is synchronous, it will work perfectly exactly as you wrote it. If fn1 has to do something asynchronous, it too will have to be asynchronous, so you will have to write it like this:
function fn1(callback) {
// do some sync stuff
doSomethingAsync(function () {
// do more sync stuff
doSomethingMoreAsync(function () {
// do more sync stuff
callback(); // call the callback once everything is completed
});
});
}
And then call it like this
fn1(function () {
fn2();
});
Which could be simplified to:
fn1(fn2);
In your first example, note that array.forEach is synchronous. If your // do many many things are all synchronous, then that will work exactly as you wrote it, but if there's an asynchronous action in there, you can either manually implement a counter, or just use the async library or Promise iterator utilities.
by adding the stdin to the callback function of the forEach function. Like:
arr.forEach(function(elem){
//do many many things
}, function() {
console.log('done, now take ur pick');
stdin.on('input', function action(){
...
});
});
Using this, the stdin and console.log functions will be called only after completion of the forEach loop.
I want to create a function, let's say a readConfig function.
readConfig(path [, callback(err, config)])
The callback here is optional. If there is no callback when called, the function will only use synchronous methods (readFile(path) and configParser(string) which return promises) to read the file and directly return the parsed config object. Exceptions are thrown directly. However when callback is provided, the function will use asynchronous methods and call the callback after finished. Exceptions are directly raised to callback but not thrown.
There are many similar codes to do sync and async work, so I want to use one method for both. How can a promise like function detect an async or sync call according to the caller's callback argument? And how can we ensure that the promise will act in synchronous way? Please show me a pattern for that.
I can't speak to the promise acting in a synchronous way, but in JavaScript you can do arguments.length to get the number of arguments passed to a function. If that equals 2 and typeof arguments[1] === 'function', then you have a second argument that is a function.
As for personal taste, I'd recommend just having 2 different functions, one of them ending in Sync, à la http://nodejs.org/api/fs.html. This module is replete with function pairs where one is async and the other isn't.
Also, remember that this is not async code:
function notAsync(cb) {
cb(null)
}
That callback executes in the same tick. You need to wrap it like so:
function async(cb) {
process.nextTick(function(){ cb(null)})
}
or something like that.
Whatever you decide, happy coding.
In this example, I'm using jQuery's deferred api. I wrap the callback in .when which allows you to attach a .then() to the callback, whether it is synchronous or asynchronous with a promise.
The solution should be to use your promise api's when. http://howtonode.org/promises
Live demo (click).
$('#sync').click(function() {
foo(function() {
});
});
$('#async').click(function() {
foo(function() {
var deferred = new $.Deferred();
setTimeout(function() {
deferred.resolve();
}, 500);
return deferred.promise();
});
});
function foo(callback) {
$.when(callback()).then(function() {
console.log('done!');
});
}