Can I report Q promise progress without creating a deferred? - javascript

I'm creating a promise by calling then.
Can I somehow report progress from inside it, or do I have to use Q.defer (which has notify)?
var promise = doSomething().then(function () {
// somehow report progress from here
});
promise.progress(function (p) {
console.log('progress', p);
});

Use deferred.notify()
It's been a while since this question had been asked, the Q library now support it.
var progress = 96;
deferred.notify(progress);
For example:
function doSomething() {
var deferred = Q.defer();
setTimeout(function() {
deferred.notify(10);
},500);
setTimeout(function() {
deferred.notify(40);
},1500);
setTimeout(function() {
deferred.notify(60);
},2500);
setTimeout(function() {
deferred.notify(100);
deferred.resolve();
},3500);
return deferred.promise;
}
doSomething()
.then(
function () {
// Success
console.log('done');
},
function (err) {
// There was an error,
},
function (progress) {
// We get notified of the progress as it is executed
console.log(progress);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/q.js/1.4.1/q.js"></script>
Progress Notification
It's possible for promises to report their progress, e.g. for tasks
that take a long time like a file upload. Not all promises will
implement progress notifications, but for those that do, you can
consume the progress values using a third parameter to then:
return uploadFile()
.then(function () {
// Success uploading the file
}, function (err) {
// There was an error, and we get the reason for error
}, function (progress) {
// We get notified of the upload's progress as it is executed
});
Like fail, Q also provides a shorthand for progress callbacks called
progress:
return uploadFile().progress(function (progress) {
// We get notified of the upload's progress
});
taken from the official readme

I'm not exactly sure what you mean by "creating a promise by calling then". I'm guessing you mean that you're returning a promise with a then defined? I.e,
var iAmAPromise = someOtherPromise.then(doSomething);
If this is the case then you can wrap the doSomething in a callback function with the appropriate notifications. A working example:
var Q = require('q');
function resolver(deferred){
return function(){
deferred.resolve('return value from initial promise');
}
}
function someOtherPromise( ms ) {
var deferred = Q.defer();
setTimeout( resolver(deferred) , ms );
return deferred.promise;
}
function doSomething(data, cb){
console.log('----- doing something with:', data);
var val = "Did something with: " + data;
cb(val);
}
function reportProgress(doSomething, notifierCb){
notifierCb('waiting on it');
return function(someOtherPromiseResponse){
console.log('--- got promise response: ', someOtherPromiseResponse);
notifierCb('got response', someOtherPromiseResponse);
console.log('--- do something with it');
notifierCb('doing something with it');
doSomething(someOtherPromiseResponse, function(val){
notifierCb('done, result was:', val);
});
};
}
function notifier(update, detail){
console.log('Notifier update:', update, detail||"");
}
function logError(err){
console.log('ERROR:', err);
}
var iAmAPromise = someOtherPromise(1000).then(reportProgress(doSomething, notifier)).catch(logError);
console.log(' (Am I a Promise?)', Q.isPromise(iAmAPromise));
I may have misunderstood your question though.

Turns out: no, I can't.
See also why it might not be a good idea after all.

Related

Using results of a callback in node in a separate function

I'm probably missing the point somewhere here so I'm looking for advice.
I have a nodejs server which is listening for client connections and, based on the data received, makes calls to an API.
The very first call to that API gets an ID which needs to be used on subsequent calls to group them together.
Where I'm struggling is that the call to the API is necessarily asynchronous and in the callback I'm assigning the ID to a variable. While that async call is being processed by the API server, more data is coming in from the client and needs more API calls made BUT I can't fire them until I know the results from the first call as the second calls depend on it.
What's the proper way to handle this? I feel like I should be using Q to promise the results of the first API call to the second, but I'm not sure how it should be structured. Or should I just be queueing up the API calls until the first completes? How would I do that?
Example problem code :
var server = net.createServer();
//set up the callback handler
server.on('connection', handleConnection);
handleConnection(conn) {
//do some stuff...
firstAPICall();
conn.on('data', handleData);
}
handleData(data) {
//do some stuff...
otherAPIcall();
}
firstAPICall() {
client.get("http://myAPI/getID", function (data, response) {
conn.myID = data[0].myID;
}
}
}
otherAPICall() {
//How do I make sure I actually have a value
//in conn.myID from the first function???
client.post("http://myAPI/storeData", { data: {myID:conn.myID, data:someData} }, function (data, response) {
//do some stuff...
}
}
}
Yes, you should be using promises for this. Make a promise for the id that is asynchronously resolved from the first call, and then use it in the subsequent calls:
handleConnection(conn) {
//do some stuff...
var idPromise = firstAPICall();
conn.on('data', function handleData(data) {
//do some stuff...
otherAPIcall(idPromise).then(function(result) {
…
});
});
}
firstAPICall() {
return Q.Promise(function(resolve, reject) {
client.get("http://myAPI/getID", function (data, response) {
resolve(data[0].myID);
});
});
}
otherAPICall(idPromise) {
return idPromise.then(function(myID) {
return new Promise(function(resolve, reject) {
client.post("http://myAPI/storeData", {
data: {myID:myID, data:someData}
}, function (data, response) {
//do some stuff...
resolve(…);
});
});
});
}
Probably you should factor out creating a promise for the result of a client.get call in an extra function. Also make sure to handle errors correctly there and call reject with them. If client would use the node callback conventions, Q even has some nice helper functions for that.
Try using promises. Then use 'then' to call the otherAPICall()
I think you can assume they will be sending data immediately after connecting. So you can simplify and just check in otherAPICall if you have an ID, if not, you can just use a callback. Promises or the async/await keywords might make things sort of nicer down the line but aren't required for this.
var server = net.createServer();
//set up the callback handler
server.on('connection', handleConnection);
handleConnection(conn) {
conn.on('data', handleData(connm, data));
}
handleData(conn, data) {
//do some stuff...
otherAPIcall(conn);
}
checkID(conn, cb) {
if (!conn.myID) {
client.get("http://myAPI/getID", function (data, response) {
conn.myID = data[0].myID;
cb();
});
} else {
cb();
}
}
otherAPICall(conn) {
checkID(conn, function() {
client.post("http://myAPI/storeData", { data: {myID:conn.myID, data:someData} }, function (data, response) {
//do some stuff...
});
});
}
promises can chain values and are always resolved after the callback occurs with the returned value,
function async(value) {
var deferred = $q.defer();
var asyncCalculation = value / 2;
deferred.resolve(asyncCalculation);
return deferred.promise;
}
var promise = async(8)
.then(function(x) {
return x+1;
})
.then(function(x) {
return x*2;
})
.then(function(x) {
return x-1;
});
promise.then(function(x) {
console.log(x);
});
This value passes through all the success callbacks and so the value 9 is logged ((8 / 2 + 1) * 2 - 1).

NodeJS Multiple function promises

Let's say I have some code that looks like this:
var doSomething = function(parameter){
//send some data to the other function
return when.promise(function(resolveCallback, rejectCallback) {
var other = doAnotherThing(parameter);
//how do I check and make sure that other has resolved
//go out and get more information after the above resolves and display
});
};
var doAnotherThing = function(paramers){
return when.promise(function(resolveCallback, rejectCallback) {
//go to a url and grab some data, then resolve it
var s = "some data I got from the url";
resolveCallback({
data: s
});
});
};
How do I ensure that var other has completely resolved before finishing and resolving the first doSomething() function? I'm still wrapping my head around Nodes Async characteristic
I really didn't know how else to explain this, so I hope this makes sense! Any help is greatly appreciated
EDIT: In this example, I am deleting things from an external resource, then when that is done, going out the external resource and grabbing a fresh list of the items.
UPDATED CODE
var doSomething = function(parameter){
//send some data to the other function
doAnotherThing(parameter).then(function(){
//now we can go out and retrieve the information
});
};
var doAnotherThing = function(paramers){
return when.promise(function(resolveCallback, rejectCallback) {
//go to a url and grab some data, then resolve it
var s = "some data I got from the url";
resolveCallback({
data: s
});
});
};
The return of doAnotherThing appears to be a promise. You can simply chain a then and put your callback to utilize other. then also already returns a promise. You can return that instead.
// Do stuff
function doSomething(){
return doAnotherThing(parameter).then(function(other){
// Do more stuff
return other
});
}
// Usage
doSomething().then(function(other){
// other
});
Below is how to accomplish what you're trying to do with bluebird.
You can use Promise.resolve() and Promise.reject() within any function to return data in a Promise that can be used directly in your promise chain. Essentially, by returning with these methods wrapping your result data, you can make any function usable within a Promise chain.
var Promise = require('bluebird');
var doSomething = function(parameter) {
// Call our Promise returning function
return doAnotherThing()
.then(function(value) {
// Handle value returned by a successful doAnotherThing call
})
.catch(function(err) {
// if doAnotherThing() had a Promise.reject() in it
// then you would handle whatever is returned by it here
});
}
function doAnotherThing(parameter) {
var s = 'some data I got from the url';
// Return s wrapped in a Promise
return Promise.resolve(s);
}
You can use the async module and its waterfall method to chain the functions together:
var async = require('async');
async.waterfall([
function(parameter, callback) {
doSomething(parameter, function(err, other) {
if (err) throw err;
callback(null, other); // callback with null error and `other` object
});
},
function(other, callback) { // pass `other` into next function in chain
doAnotherThing(other, function(err, result) {
if (err) throw err;
callback(null, result);
})
}
], function(err, result) {
if (err) return next(err);
res.send(result); // send the result when the chain completes
});
Makes it a little easier to wrap your head around the series of promises, in my opinion. See the documentation for explanation.

Stubbing a function in sinon to return different value every time it is called

I have a function as shown below:
function test(parms) {
var self = this;
return this.test2(parms)
.then(function (data) {
if (data) {
return ;
}
else {
return Bluebird.delay(1000)
.then(self.test.bind(self, parms));
}
}.bind(self));
};
I am trying to write unit tests for this function. I am using sinon.stub to mock the functionality of the function test2.
I wrote a test case where test2 returns true and therefore the test function successfully completes execution. However I want a test case where on the first instance test2 returns false, it waits for delay and next time test2 returns true. For that I wrote my test case as below:
var clock;
var result;
var test2stub;
var count = 0;
before(function () {
clock = sinon.useFakeTimers();
//object is defined before
test2stub = sinon.stub(object,"test2", function () {
console.log("Count is: " + count);
if (count === 0) {
return (Bluebird.resolve(false));
}
else if (count === 1) {
return (Bluebird.resolve(true));
}
});
clock.tick(1000);
object.test("xyz")
.then(function (data) {
result = data;
});
clock.tick(1000);
count = count + 1;
clock.tick(1000);
});
after(function () {
test2stub.restore();
clock.restore();
});
it("result should be undefined. Check if test2 returned false first & true next",
function () {
expect(result).to.be.undefined;
});
In the logs it shows that count has value 0 only.
The code of test is actually incorrect. It never returns data on success. It returns undefined. The function should return data on success otherwise you won't be able to use it as parameter for next .then handler
.then(function (data) {
if (data) {
return data;
}
Next you make wrong assumptions about function test. It will NEVER return undefined. The function is rather dangerous and will call itself forever in an endless chain of promises until it squeezes out any not-null data from test2.
One shouldn't launch test code in before or beforeEach section. before and after are meant to prepare the environment like faking timers and then restoring them.
One reason for calling tested code in the it handler is because promises should be handled differently. The handler should accept a parameter which indicates that the test will be asynchronous and the the test engine gives it a timeout (usually 10 secs) to complete. The test is expected to either call done() to indicate test is successful or call done(error) if it failed and there is an error object (or expect threw an exception).
Also you should move the fake timer after the async operation started. In your code actually the first clock.tick is useless.
There is a trick with using fakeTimers. You can move time manually however it doesn't move on its own. For the first tick it works well. The promise is executed. However upon returning .delay(1000) promise, there will be no command to move the time forward. So, to finish the test correctly (not to modify tested code) you have also to stub Bluebird.delay
I would change the stubs implementation and do something like this
describe("test2", function(){
beforeEach(function(){
clock = sinon.useFakeTimers();
test2stub = sinon.stub(object,"test2", function () {
console.log("Count is: " + count);
return (Bluebird.resolve((count++) > 0));
});
var _delay = Bluebird.delay.bind(Bluebird);
bluebirdDelayStub = sinon.stub(Bluebird,"delay", function (delay) {
var promise = _delay(delay);
clock.tick(1000);
return promise;
});
})
it("should eventually return true", function (done) {
object.test("xyz")
.then(function (data) {
expect(data).to.be.true;
expect(count).to.equal(2);
done();
})
.catch(function(err){
done(err);
});
clock.tick(1000);
});
after(function () {
test2stub.restore();
clock.restore();
bluebirdDelayStub.restore();
});
})
PS I verified this code under Node.js 0.10.35 and Bluebird 2.9.34

Bluebird Promising the result of a heavy function

I've been using Bluebird a lot recently on a HAPI API development. I've just run into my first real problem, that perhaps my understanding or naivety has me stumped.
The following code is an example of what I am facing:-
var Promise = require('bluebird'),
stuff = require('../stuff');
module.exports = {
getSomething: function(request, reply) {
var p = Promise.resolve();
p = p.then(function() {
return db.find() //etc. etc.
});
p = p.then(function(resultFromPromise) {
//problems begin here
var data = stuff.doSomeReallyLongAndBoringFunction(resultFromPromise);
return data;
});
p.then(function(data) {
//no data here.
});
};
};
I've commented where the problems usually begin. the stuff.doSomeReallyLongAndBoringFunction() returns an object (using more promises concidently) and it's this object I want to access, but //no data here always fires before data returns. stuff.doSomeReallyLongAndBoringFunction() continues to run regardless and completes successfully, but after the code goes async, I don't know how to promise that function's return value back.
Can anyone offer any guidance? Please accept my apologies for any naivety in the question!
Help as always, is appreciated
NB just for clarity, stuff.doSomeReallyLongAndBoringFunction() does not return a Promise itself. Although, I did try return new Promise(reject, resolve) { }); manual wrap. It is simply a function that uses promises itself (successfully) to get data.
Update 1
stuff.doSomeReallyLongAndBoringFunction() is too big to post directly, but it does something like this:-
var Promise = require('bluebird'),
rp = require('request-promise');
module.exports = {
doSomeReallyLongAndBoringFunction: function() {
var p = Promise.resolve();
p = p.then(function() {
return db.find() //etc. etc.
});
p.then(function() {
rp(options).then(function(response){
//get some data from remote location
}).then(function(dataFromService) {
//do some jiggery pokery with said data
var marshalledData = dataFromService;
db.something.create({
Field: 'something'
}).exec(function(err, saved) {
return marshalledData;
});
});
}).catch(function(err) {
});
};
};
Update 2
Thank you Justin for your help. Here is the actual code, perhaps this may help?
Promise.resolve()
.then(function() {
if(typeof utils.intTryParse(place) !== 'number') {
return foursquare.createPlaceFromFoursquare(sso, place, request, reply);
} else {
return { Place: { PlaceId: place }};
}
}).then(function(placeObj) {
console.log('Place set as', placeObj); //always returns undefined, despite function actually completing after async op...
});
If your doSomeReallyLongAndBoringFunction is really running asynchronously, then it doesn't make sense to run it the way you have setup.
Edit - Here's a simple explanation of the way your code looks to be running vs a refactored version. It's been simplified , so you'll need to fill in the relevant sections with your actual implementation.
var Promise = require('bluebird');
function myAsync() {
setTimeout(function(){
return 'done sleeping';
}, 2000);
};
//The way your code is running
Promise.resolve()
.then(function(){
return 'hello';
})
.then(function(done){
console.log(done);
return myAsync(); //your error is here
})
.then(function(done){
console.log(done);
});
//refactored
Promise.resolve()
.then(function(){
return 'hello';
})
.then(function(done){
console.log(done);
return new Promise(function(resolve) {
setTimeout(function(){
resolve('done sleeping');
}, 2000);
});
})
.then(function(done){
console.log(done);
});
just for clarity, stuff.doSomeReallyLongAndBoringFunction() does not return a Promise itself.
And that's your problem. As it does something asynchronous and you want to get its result, it should return a promise. In fact, that's the case for every asynchronous function, especially then callbacks! It should be something like
module.exports = {
doSomeReallyLongAndBoringFunction: function() {
return db.find()
// ^^^^^^
.then(function() {
return rp(options).then(function(response){
// ^^^^^^
//get some data from remote location
}).then(function(dataFromService) {
//do some jiggery pokery with said data
var marshalledData = dataFromService;
return db.something.create({
// ^^^^^^
Field: 'something'
}).execAsyc();
});
}).catch(function(err) {
});
}
};
Your getSomething method has the same issues, and should look like this:
var createPlace = Promise.promisify(foursquare.createPlaceFromFoursquare);
module.exports = {
getSomething: function(request) {
var p;
if (typeof utils.intTryParse(place) !== 'number')
p = createPlace(sso, place, request); // this returns a promise!
else
p = Promise.resolve({Place: {PlaceId: place}});
return p.then(function(placeObj) {
// ^^^^^^
console.log('Place set as', placeObj);
});
}
};
See also these generic rules for promise development.
doSomeReallyLongAndBoringFunction needs to look like this:
doSomeReallyLongAndBoringFunction: function(param) {
var resolver = Promise.defer();
/*
* do some asynchronous task and when you are finished
* in the callback, do this:
*/
resolver.resolve(resultFromAsyncTask);
/*
*
*
*/
return resolver.promise;
}

Async.js return promise

I'm using the async.js library to achieve an stream of asynchronous requests.
The code below works fine.
async.each(
//--- Collection of models to save ---//
collSaveData,
function(model, callback){
model.save([], {success: function(){
callback();
}});
},
function(err){
console.log('finished');
});
How can I return a promise?
I mean, something in a fashion like this:
var promise = async.each(
//--- Collection of models to save ---//
collSaveData,
function(model, callback){
model.save([], {success: function(){
callback();
}});
},
function(err){
console.log('finished');
});
You probably don't need async.js to issue you calls and synchronize them. Combine the objects returned by Model.save with $.when to produce a general promise :
var promises = _.invoke(collSaveData, 'save');
var promise = $.when.apply(null, promises);
promise.then(function() {
console.log('all done');
});
And a Fiddle http://jsfiddle.net/nikoshr/Z3Ezw/
You can customize how you handle the responses from each save, for example:
var promises = _.map(collSaveData, function(m) {
return m.save().then(function(response) {
console.log('saved', m);
});
});
The key is to return a promise for each model. http://jsfiddle.net/nikoshr/Z3Ezw/2/
Not actually used async before but looking through the docs it makes no reference to returning a promise so you would have to wrap the asyn.each with a function that did return a promise and then the success/error callback could then just resolve or reject that promise
here is a quick example that should work
//wrap the async each and return a promise from this call
var promiseAsync = function(openfiles, saveFile) {
var defer = $.Deferred();
async.each(
openfiles, saveFile, function(err) {
if (err) {
defer.reject(err);
} else {
defer.resolve();
}
});
return defer.promise();
}
//now it can be used like a normal promise
var promise = promiseAsync(collSaveData, function(model, callback) {
model.save([], {
success: function() {
callback();
}
});
});
$.when(promise).done(function(){
//whatever
}).fail(function(err){
//error
});
make promiseAsync available throughout your app (attach it to Backbone somewhere, underscore mixin,helper/utility module??) and then you can always use it.
Update: fiddle based on #nikoshr's fiddle (for setting up the page) (going to start using fiddles over code pen now like the fact you can have a console in the browser) http://jsfiddle.net/leighking2/7xf7v/

Categories