Promisifying Zookeeper's multiple callback function - javascript

Zookeeper provides a getChildren method that takes in the path of the node and returns the children of that node in a callback. It also sets a watch during the process and calls the watcher callback when a watch is triggered
getChildren(path, function(err,event){
//this is the watcher callback
},
function(err,children,stats){
//children callback
}
)
So if I use the bluebird's Promise.promisify to promisify this function. How do I know that the promise this function is returning is the watcher or the children ??

If I understand the getChildren() interface correctly, the last callback is designed to be called once with the list of child objects. The first callback is a watcher callback that may be called an indefinite number of times to notify you of various changes occurring.
Given that, the last callback could fit with a promise. The first callback cannot and must remain a callback. In addition, the second callback is returning multiple results (which does not fit perfectly with promises) so you have to make allowances for that too using multiArgs and .spread.
So, you could do something like this:
let getChildrenP = Promise.promisify(getChildren, {multiArgs: true});
getChildrenP(path, function(err, event) {
// watcher event occurred here
// this may get called multiple times
}).spread(function(children, stats) {
// children are available now
}).catch(function(err) {
// error here
});

Related

What is the difference of callback and module exports?

There is an article I've seen about the callbacks in javascript. https://codeburst.io/javascript-what-the-heck-is-a-callback-aba4da2deced I know that I can understand it by reading the article. However, I'm getting confused of the callback while studying the module export in node.js
Callback - A callback is a function that is to be executed after another function has finished executing
Callback in javascript
function doHomework(subject, callback) {
console.log(`Starting my ${subject} homework.`);
callback();
}
doHomework('math', function() {
console.log('Finished my homework');
});
Module export in node.js
//app.js
const logger = require('./logger');
logger.log(10, 10);
//logger.js
const multiply = require('./multiplication');
function log(valueOne, valueTwo) {
multiply('The result is ', valueOne, valueTwo);
}
module.exports.log = log;
//
function multiply(speech, valueOne, valueTwo) {
let result = valueOne * valueTwo;
return console.log(speech + result);
}
module.exports = multiply;
and ran the node app.js on my terminal.
The result that I got from running the node app.js is The result is 100 and that is correct.
But my question is
Does the approach that I did on the node app is consider as callback as well?
Callback - A callback is a function that is to be executed after another function has finished executing
That's not a correct definition of "callback." Unless that definition had major caveats on it you didn't quote, I wouldn't continue to use whatever resource you got that from.
A fairly broad definition of a callback is:
A callback is a function you pass to something else (as a function argument, property value, etc.) that the other thing will call when its defined criteria for calling the function are met.
Some might argue for a narrower definition:
A callback is a function you pass to another function for that other function to call back when its defined criteria for doing so are met.
Examples of callbacks:
DOM event handlers, although we usually call them "handlers" rather than callbacks. (Broad definition.)
The function you pass to Array.prototype.sort to compare array elements. (Both the broad and narrower definitions.)
The function you pass to new Promise to start the asynchronous operation the promise will observe (called the "promise executor function"). (Both the broad and narrower definitions.)
The function you pass to Array.prototype.map to transform elements. (Both the broad and narrower definitions.)
The function you pass to a promise's then, catch, or finally method.
The function you pass to fs.openFile that Node.js will call when the file has been opened (or the operation has failed). (Both the broad and narrower definitions.)
...and many others.
Notice that many of those (2, 3, and 4) are called before the function calling them has finished executing.
Does the approach that I did on the node app is consider as callback as well?
No. Although you use multiply in log, it's just a function you call from log, not a callback. This would be a callback:
function multiply(a, b, cb) {
cb(a * b);
}
function showResult(msg) {
console.log(msg);
}
multiply(7, 6, showResult);
showResult is used as a callback when calling multiply.
I don't entirely understand your question. However, from what I gather, module.exports does not make a function a callback function explicitly. The purpose of module.exports is to allow access to that function when requiring the relevant .js file...as seen in your example.
Your log() function is a not a callback as you are simply passing in parameters and then using those values to call the multiply function and output the result.
When you call the multiply function you are simply calling it like so:
multiply('some text', 10, 10)
For this to be a callback it would have to take a function as it's final parameter, i.e.:
multiply('some text', 10, 10, function(err, data) {
// ...
})
This also goes for the log function, and any for that matter.
So, unless the final parameter of a function is a function, it is a not a callback. module.exports purely allows access to that function or the functions you specify in the object, for example:
module.exports = {
functionOne: someFunctionName,
functionTwo,
functionThree
}
If the name of the function is the same name as what you are trying to export you do not need to specify a value to the key.

Javascript callback function and an argument in the callback. How must it be used based on the code snippet provided?

I'm reading over a legacy codebase and I ran into this following code:
andThenWe: function(callback) {
var qunitAssertAsync = new window.AssertAsync(callback);
return qunitAssertAsync;
},
and here's the call site:
andThenWe(function(done) {
...(some code)
done();
});
So in the call site, we're passing in an anonymous function which will then be === 'callback' right? However, this callback has an argument called done and seems to be called at the end of this function. That argument is kind of like a block parameter in Ruby right? So somewhere in the window.assertAsync the callback MUST be called and passed some kind of arugment which is probably === to Qunit's assert.async right? (most likely). The details of the window.assertAsync are really complicated so I just want to understand at a high level what must be going on. Am I making proper assumptions?
This is all possible because callback in the function signature is an anonymous function that be invoked at a later time right? Also done itself in the callback function must be a function itself at runtime right?
I think this is an attempt to make qunit.async more "readable" (haha).
qunit.async is used to force tests to wait until an async operation has completed before exiting the test.
The done callback must be invoked when the writer of the test knows everything async has completed.

How do I create an observable from the onmessage callback?

I am fairly used to RX having used it in .NET and Java, I am expecting to be able to do the following:
Rx.Observable.fromCallback(websocket.onmessage)
.map(...)
.subscribe(...);
however, the console has the following:
Uncaught TypeError: Rx.Observable.fromCallback(websocket.onmessage).map is not a function
which would appear to indicate that the fromCallback is not returning an Observable.
What am I doing wrong here? Have I misunderstood what fromCallback is doing and I need to use a Subject? Can I not wrap some arbitrary handler in an observable?
You are actually looking for fromEvent or fromEventPattern:
Rx.Observable.fromEvent(websocket, 'message').map(/*...*/).subscribe();
Rx.Observable.fromEventPattern(
function add(h) { websocket.addEventListener(h); },
function remove(h) { websocket.removeEventListener(h); })
.map(/*...*/)
.subscribe();
The first one will attempt to use some of the standard ways of subscribing to an event emitter, which WebSocket is. However, if that fails you can use fromEventPattern instead to specify how handlers are added or removed from your object.
One additional note, JavaScript does not pass along an implicit reference to the instance of the object you are using as C# and Java do, so your code fromCallback(websocket.onmessage) is not passing along websocket, it is passing along the reference to the method from the function prototype. this will be determined at execution time.
Rx.Observable.fromCallback is for functions whose last argument is a callback function which is a standard pattern for asynchronous JavaScript code. Further, the fromCallback method does not return an Observable it returns a function that when called returns an Observable i.e.
function methodWithCallback(arg0, arg1, cb) {
setTimeout(function() {
cb(arg0 + arg1);
}, 2000);
}
var newMethod = Rx.Observable.fromCallback(methodWithCallback);
//[After 2 seconds] 3
newMethod(1, 2).subscribe(console.log.bind(console));

referencing the same object inside another callback from forEach

I'm trying to add a new property to an object. It seems that it works correctly from this scope:
rows.forEach(function (row, i) {
row.foo = i;
});
If I do console.log(rows) I can see that foo was added with the correct value. If I have another callback within the forEach, I don't see the change any more. Why?
rows.forEach(function (row, i) {
getUserById(row.user_id, function(user) {
row.foo = i;
});
});
Yes, the callback get's fired correctly. Here is the getUserById
function getUserById(userId, callback) {
connection.query('select * from t_user where id = ?', [userId], function(err, results) {
if (err) {
console.log("repo error");
} else {
if (results.length == 0) {
callback(null);
} else {
callback(results[0]);
}
}
});
}
I can only image that you would be seeing this issue if getUserById defers calling the callback function you pass it. On that assumption, your problem is a timing issue.
Each time you call getUserById and pass it a callback, you are basically saying "Call this function when you get the user for this id", and then going along with the rest of your program. That means that when console.log is called, your callbacks have not yet been called. Instead, you have to wait for all of your callbacks to finish, and then call any code that relies on the values you are setting in your callback.
The general approach to waiting for all your callbacks to finish is to craft a special callback function which will delegate to each callback and also keep track of once they've all been called.
Here is a question with an answer using the async library, which I'm a fan of (though there are a number of libraries to tackle this control flow problem).
Idiomatic way to wait for multiple callbacks in Node.js
Here's a raw JavaScript solution that is robust enough to work in the general case where not all the callbacks are the same (that is, if you wanted a different callback for each user id).
Multiple Asynchronous Callbacks - Using an array to fire function when complete

How to collect multiple callbacks to call once a task is done

I can't seem to find a general solution to this problem, even though I feel like I can't be the first to come across this. I also suspect it might be I'm taking the wrong approach in the first place here. Let me know.
I have an Expressjs App the interacts with various APIs (mostly OAuth2). Once a request comes in the App checks if it has an access token to fetch data from an API. In case the token is expired it will request a new one.
Now, when my App receives a second request in the meantime, requiring the exact same API, I want to avoid making a second call for an access token.
What I do is use a "Collector" where callbacks can be stored for a given key. The first request to store a callback for a key gets a collector callback to invoke once it has finished the task. All subsequent callbacks are enqueued and called later on with the collector callback.
This is the simple Collector class (CoffeeScript)
# Collect callbacks for tasks and execute all once the job is done
module.exports = class Collector
constructor: ()->
#tasks = {}
add: (key, callback)->
unless #tasks[key]
# Initiate callback list for the key with first callback
#tasks[key] = [callback]
return ()=>
# Call all collected callbacks for the key
(callback.apply {}, arguments for callback in #tasks[key])
# Reset callback list
#tasks[key] = null
else
# Add callback to existing list
#tasks[key].push callback
return false
I'm not sure if storing the callbacks inside this class is the right way, but to use a database (Redis) I would have to find a way to store callbacks…
Is there a better way to invoke multiple callbacks once a job is done?
Why don't you just aggregate your callbacks into an array which you cycle through, executing each contained function when your original call is complete?
It could be as simple as:
var dones = [];
dones.push(function (err, res) { console.log('hoo'); });
dones.push(function (err, res) { console.log('ray!'); });
function whenDone(err, res) {
_.each(dones, function (done) { done(err, res); } });
}
myWhateverFunction(whenDone);
You can wrap this into whatever data structure you want, if you want to make it prettier.
I don't have a specific answer to your problem, but you should check out the async module. I think it's a step in the right direction: https://github.com/caolan/async

Categories

Resources