I have a situation where I am creating a node module that returns only when an asynchronous operation is completed. One way to do this (shown below), is to assign module.exports a function with a callback parameter. Inside the function, you would then return the callback.
Here's an example of what I describe, with done being callback:
// module called test.js
module.exports = function(done) {
// do something asynchronous here
process.nextTick(function() {
done(); // call done when the asynchronous thing is complete...
}
}
Where I am getting hung up, is in how the callback done is indeed being executed, considering I don't define it anywhere...
For example, in vanilla javascript, I can pass done as parameter and then call it within the function, as long as I create the callback function in the invocation.
function testAsyncCb(msg, done) {
console.log(msg);
setTimeout( function() {
done();
}, 1000);
console.log("last line in code");
}
testAsyncCb("testing", function(){ console.log("done"); }); // invocation with callback function
Back in the first node example, somewhere the call to module.exports by require() is creating a function for the done() to resolve to right? If not, how is the callback resolving?
Having a hard time finding information for how this works. Any help/direction is appreciated.
Think of module.exports as an object (module.exports = {}). So whatever you put to the object will be publicly visible to anyone do require module.
For instance, you have
module.exports = function myFunc() {}
then require to that would mean
var abc = require('./my-module'); --> abc == myFunc
if you would do
module.export.myFunc = function () {}
than require would be
var abc = require('./my-module'); --> abc == {myFunc: function () {}}
require operation is sync, not async like in requirejs (meaning not AMD but more like commonjs).
see http://www.sitepoint.com/understanding-module-exports-exports-node-js/
for more info
also for nodejs official docs: https://nodejs.org/api/modules.html
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'm sure the answer to this question exists somewhere, but unsure how to phrase the question and don't find what I'm looking for when I try to research callbacks.
Ok, so I've just started dabbling with Gulp for compiling my CSS, and I think it's great!.. despite being useless with JavaScript.
My code is below, but I don't understand how these callbacks are working. How can callbacks be set as a parameter and then be called from inside the function? I don't get it.. is the function basically expecting something to be in there? What value is being set or what's expected to run? I can't seem to make sense of it.
I see this quite frequently in JavaScript, but unsure how it works. I've looked up videos and tutorials of functions and callbacks, they makes sense, but I never seem to be able to find anywhere where this concept is explained which makes me believe I'm not looking for the right thing.
I see the same sort of thing with Promises as well where 'resolve' or 'reject' parameters are set, but unsure what's going on when a condition is met, or what values are set and where.. hope this makes sense and appreciate any help in understanding this better.
const gulp = require('gulp');
const sass = require('gulp-sass');
const browserSync = require('browser-sync');
// TASKS
// Compile SASS
gulp.task('sass-compile', (callback) => {
gulp.src('scss/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('css'))
.pipe(browserSync.stream());
console.log('******************** SCSS > CSS successful ********************');
callback();
});
// Live reload
gulp.task('browser-sync', (callback) => {
browserSync.init({
proxy: 'http://localhost/test1',
port: 80
});
callback();
});
// WATCHER
gulp.task('default', gulp.series('browser-sync', (callback) => {
gulp.watch('scss/*.scss', gulp.series('sass-compile'));
callback();
}));
function foo(baz) {
console.log("Foo: " + baz);
}
function other(bar) {
const arg = "Bar";
bar(arg);
}
other(foo);
I can see how this is useful when it comes to performing asynchronous tasks. But its quite counter intuitive and I can immediately see the potential for type errors.
I've since changed my coding style to use ASYNC/AWAIT instead as much as possible. But it still a pain to navigate legacy code. Hence, the reason why I ended up looking up this thread. Haha
Functions are a type of object. Objects are a type of value.
Values can be stored in variables, in object properties, and passed as arguments to functions.
So:
Here is a simple function, which is called.
function foo() {
console.log("Foo");
}
foo();
Here is a simple function, copied to a different variable, which is called.
function foo() {
console.log("Foo");
}
const bar = foo;
bar();
And now passed as an argument to another function.
function foo() {
console.log("Foo");
}
function other(bar) {
bar();
}
other(foo);
And the same, but with arguments passed to the original function.
function foo(baz) {
console.log("Foo: " + baz);
}
function other(bar) {
const arg = "Bar";
bar(arg);
}
other(foo);
You've just been looking at examples where the function responsible for calling the callback is not code you've written (and you aren't looking at the source code of the function which calls the callback).
In javascript, functions are first class objects, which means they can be passed around to other functions. There are many cases where this can be used, but the cases you're referring to (gulp.task and new Promise) are essentially using it to do two-way communication between your code and their code. When you use gulp.task, basically the following steps are happening
1) You call gulp.task, saying "i'd like to do some work please". You pass in a function saying what you'd like to do
2) at the appropriate time, gulp.task calls your function, but passes in to it another function saying "when you're done, call this to let me know".
A simplified version of gulp.task would look like this:
const task = (name, workToDo) => {
const done = () => {
console.log('task', name, 'has let me know its done');
}
console.log('ive been asked to start task', name);
setTimeout(() => {
console.log('500ms elapsed; starting task', name);
workToDo(done);
}, 500);
}
task('sample', (done) => {
console.log('doing work');
setTimeout(() => {
console.log('1 second has elapsed. calling back to let it know i am done');
done();
}, 1000)
});
The constructor for a promise has a similar purpose. You say "i'd like to create a promise, using the following code", and then it calls your code passing in two functions that it just created (usually named resolve and reject). These are used to tell the promise "i'm done successfully" or "i'm done with an error", and thus move the promise to a resolved state or a rejected state.
I tried to execute a series of events in my Node.js project with async as follows:
server.js:
var express = require('express');
var async = require('async');
...
app.get('/my_page', function (req, res) {
async.series([
function() { console.log("calling foo()"); },
function() { foo(); },
function() { console.log("foo() done"); },
]);
res.render('my_page', {});
}
but I only get the first console output and it's stuck.. the function()s were function(callback)s before. I thought it was waiting a value to be returned but removing them doesn't change the situation..
What am I doing wrong? (I'm newbie to Node.js)
Thanks for any help,
Please read the documentation. Each function should get callback as a parameter, and should call callback once it's done, thus telling async it's time to move on to the next function. So you should have something like:
function(callback) {
console.log("calling foo()");
callback();
},
If there was an error in one of the functions, call callback with the error as the 1st parameter. If you want res.render('my_page', {}); to be executed only after the last function is executed, wrap it in a function and put it as the 2nd parameter to async.series.
And of course, if non of the functions is asynchronous, you should consider not using async.
Firstly, I'm not sure why you're using async here, since none of the functions you've provided to the async.series function are asynchronous.
async.series is useful for calling multiple asynchronous functions in a row and avoiding callback hell.
var async = require("async");
function asyncA(callback){
// do something
callback(null, "value-a");
}
function asyncB(callback){
// do something else
callback(null, "value-b");
}
asyncA(function(err, valueA){
console.log(valueA); // "value-a"
asyncB(function(err, valueB){
console.log(valueB); // "value-b"
});
});
async.series([asyncA, asyncB], function(err, results){
console.log(results); // ["value-a", "value-b"]
}
I suggest doing some reading on asynchronous functions and callbacks and consider whether you really need the async library. I recommend the You Don't Know JS chapter on asynchronous javascript and the series as a whole :)
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 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.