How to use defer() the right way? - javascript

How do I use defer correctly? I've got two functions here, in which one defer is used correctly? In which one it is used incorrectly? And why resp. why not?
First example:
getFoo1: function() {
var dfd = $q.defer();
db.allDocs({include_docs: true}, function(err, response) {
if(err) {
console.log(err);
dfd.reject(err);
} else {
var qcs = [];
for(var i=0; i < response.total_rows; i++) {
if(response.rows[i].doc.type == 'bar') {
var qc = {id: response.rows[i].doc._id,
isFoo: true
};
oneFunction(qc)
.then(anotherFunction(qc))
.then(qcs.push(qc));
}
}
dfd.resolve(qcs);
}
});
return dfd.promise;
},
Second example:
getFoo2: function() {
var dfd = $q.defer();
db.allDocs({include_docs: true}, function(err, response) {
if(err) {
dfd.reject(err);
} else {
dfd.resolve(response);
}
});
return dfd.promise
.then(function(response) {
var foo = [];
for(var i=0; i < response.total_rows; i++) {
if(response.rows[i].doc.type == 'bar') {
var qc = {id: response.rows[i].doc._id,
isFoo: true
};
return oneFunction(qc)
.then(anotherFunction(qc))
.then(foo.push(qc));
}
}
}, function(err){
console.log(err);
});
},
The oneFunction does nothing asynchronously.
The anotherFunction does something asynchronously (retrieving data from an external database).
EDIT: Thanks to #Bergi the correct function should look like this:
getFoo3: function() {
var dfd = $q.defer();
db.allDocs({include_docs: true}, function(err, response) {
if(err) {
dfd.reject(err);
} else {
dfd.resolve(response);
}
});
return dfd.promise
.then(function(response) {
var foos = [];
for (var i=0; i < response.total_rows; i++) {
if (response.rows[i].doc.type == 'bar') {
var qc = {id: response.rows[i].doc._id,
isFoo: true
};
var foo = oneFunction(qc);
foos.push(foo);
}
}
var promises = foos.map(anotherFunction); // make a promise for each foo
return $q.all(promises);
}, function(err){
console.log(err);
});
},

You've used $q.defer correctly in the second example[1] - creating a promise for the response of the db.allDocs asynchronous call (and nothing else). Altough pochdb seems to already return promises, as #Benjamin mentions in the comments, so it's unnecessary (but not wrong).
The first example is just a mess, convoluting the construction of the promise with the error logging and that ominous loop.
1: Except for dfd.promise(), which is not a function but a property. Go for dfd.promise.then(…).
However, that loop is a very different topic, and seems to be wrong in both snippets. Some points:
In the second snippet, your return from the callback function in the body of the loop, right on the first iteration that fulfills the predicate.
If oneFunction(qc) is not asynchronous, it doesn't need to (read: shouldn't) return a promise - or if it does not, that .then(…) call is wrong.
anotherFunction(qc) seems to return a promise (it's asynchronous as you say). But you must not pass that promise to .then(), a callback function is expected there!
Same thing for foo.push(qc) - it's not a callback function that you would pass to .then().
After all, you are doing something asynchronous in that loop. Here, you have to use Promise.all now!
If I had to bet, I'd say you need
.then(function(response) {
var foos = [];
for (var i=0; i < response.total_rows; i++) {
if (response.rows[i].doc.type == 'bar') {
var qc = {id: response.rows[i].doc._id,
isFoo: true
};
var foo = oneFunction(qc);
foos.push(foo);
}
}
var promises = foos.map(anotherFunction); // make a promise for each foo
return $q.all(promises);
})

Related

stop/exit for loop on first reject promise in angular JS

I have tried all various way to do so but still didnt figure out how to resolve this.
I have a method iterator that return promise; this method runs for x times in a loop. I want if iterator return rejected promise than it should stop the execution and goes to error bloc.
I have tried with below approach taken from this SO asnwerbut this send the error but execute till the x times.
var foo = function(formdata) {
var d = $q.defer();
CallService.init()
.then(function(response) {
return CallService.fun1(formdata);
})
.then(function(response) {
var row = {1:'one', 2:'two', 3: 'three' };
var promiseArray = [];
if (<key condition true>) {
var promise = Object.keys(row).map(function(i) {
return iterator(i, row[i]).then(function(response){
promiseArray.push(response);
}, function(err){
return $q.reject(err);
});
});
return $q.all(promise); // reject if anyone is rejected, otherwise resolve
}
else {
d.reject({
message: "key condition failed."
});
}
return d.promise;
})
.then(successHandler)
.catch(errorHandler);
};
and here is the function that being called for each loop
var iterator = function(num, data) {
var d = $q.defer();
CallService.fun3()
.then(function(response) {
return CallService.fun4(data)
})
.then(function(response) {
d.resolve(num);
})
.catch(function(err) {
d.reject(err);
});
return d.promise;
};
I want that if iterator send reject for the first time than it should not go for the next and break the loop;
how to achieve that?
I have also tried this way
var d = $q.defer();
for (var k in row) {
if (isCalled) break;
var promise = iterator(k, row[k]);
promise.then(function(response) {
promiseArray.push(response);
}, function(err) {
isCalled = true;
d.reject(err);
});
}
if (!isCalled && promiseArray.length > 0) {
console.log('after for loop', promiseArray);
d.resolve(promiseArray);
}
return d.promise;
but this also fails.. it always executes for all the loop in spite of any reject promise.
edit
I have tried with .reduce method but this is somehow works but I need to send promiseArray ( if any entry is there ) alog with the error. here is my working code.
var first_promise = $q.resolve();
var start_promise = Object.keys(row).reduce(function(promise, key){
return promise.then(function(result){
if(angular.isDefined(result)) // as first_promise results undefined
promiseArray.push(result);
return iterator(key, row[key]);
});
}, first_promise);
start_promise.then(function(res){
return $q.when(promiseArray);
}).catch(function(err){
return $q.reject();
});
Updated answer after clarification. Op wants his asynchronous calls to run synchronous. Stopping when they hit a rejection.
Check out this blogpost. That decorator seems to do exactly that.

Wait for promises inside of a angular.forEach loop

I know this has been asked quite a few times already but after a day of search I still don't get it to work, although it's just like what is shown as a solution everywhere...
I have a async request to a database which returns an array of data. For each object in this array I need to start another async request to the database and as soon as ALL of these async requests resolve, I want to return them. I read you could do it with $q.all(...)
So here's the code:
Factory.firstAsyncRequest(id).then(function (arrayWithObjects) {
var promises = [];
var dataArr = [];
angular.forEach(arrayWithObjects, function (object, key) {
var deferred = $q.defer();
promises.push(deferred);
Factory.otherAsyncRequest(key).then(function (objectData) {
dataArr.push({
name: objectData.name,
key: key,
status: objectData.status
});
deferred.resolve();
console.info('Object ' + key + ' resolved');
});
});
$q.all(promises).then(function () {
$rootScope.data = dataArr;
console.info('All resolved');
});});
From the console I see that the $q.all is resolved BEFORE each object. Did I get something wrong? This seems to work for everyone...
Your help is highly appreciated, been looking the whole night, it's 5:30am now lol..
Cheers
EDIT:
So for anyone who's coming here later: It was just the promises.push(deferred.PROMISE) bit. Tho, I read that anguar.forEach is actually not a recommended method to loop through array because it was originally not constructed to be used by the end-user. Don't know if that's correct but I figured out another way if you don't want to use angular.forEach:
Users.getAll(uid).then(function (users) {
var uids = ObjHandler.getKeys(users); //own function just iterating through Object.keys and pushing them to the array
var cntr = 0;
function next() {
if (cntr < uids.length) {
Users.getProfile(uids[cntr]).then(function (profile) {
var Profile = {
name: profile.name,
key: uids[cntr],
status: profile.status
});
dataArr[uids[cntr]] = Profile;
if(cntr===uids.length-1) {
defer.resolve();
console.info('Service: query finished');
} else {cntr++;next}
});
}
}
next();
});
And the getKey function:
.factory('ObjHandler', [
function () {
return {
getKeys: function(obj) {
var r = [];
for (var k in obj) {
if (!obj.hasOwnProperty(k))
continue;
r.push(k)
}
return r
}
};
}])
Instead of
promises.push(deferred);
Try this:
promises.push(deferred.promise);

Javascript for loop Promises

I have an array of urls like this
var urls = ["www.google.com", "www.yahoo.com"];
And I want to loop though the urls and perform an async task inside the loop and not move on to the next item until the async task has finished. I know you can do this with promises but I have having some trouble with it. Here what I have
var xmlReader = require('cloud/xmlreader.js');
function readResponse_async(xlmString) {
var promise = new Parse.Promise();
xmlReader.read(xlmString, function (err, res) {
if(err) {
promise.reject(err);
} else {
promise.resolve(res);
}
});
return promise;
}
for (i = 0; i < urls.length; i++) {
Parse.Cloud.httpRequest({
url: unionUrls[i],
}).then(function(httpResponse) {
try {
// console.log(httpResponse.text)
return readResponse_async(httpResponse.text)
} catch (e) {console.log(e)}
}
But right now it doesn't wait for the readResponse_async to finish, how can I have it wait for that?
Thanks
EDIT
After reading the response I make a save to my database and I have another array like this
var location = ['USA', 'England'];
And I make the save like this
function saveLoc_async(data, location) {
var i3, i4, i5, m,
TestItem = Parse.Object.extend("TestItem"),//can be reused within the loops?
promise = Parse.Promise.as();//resolved promise to start a long .then() chain
for (i3 = 0; i3 < data.count(); i3++) {
(function(testItem) {
testItem.set("item", data.at(i));
testItem.set("location", location);
//build the .then() chain
promise = promise.then(function() {
return testItem.save();
});
})(new TestItem());
//************************
//CALL retry(); here?
//**************************
}
Because with your answer I have
function retry() {
if (urlsUnion.length > 0) {
var nextUrl = urlsUnion.pop();
//********** ADDED LINE
var nextLoc = location.pop();
Parse.Cloud.httpRequest({
url: nextUrl,
}).then(function(httpResponse) {
xmlReader.read(httpResponse.text, function (err, res) {
if(err) {
// show an error
} else {
//********** ADDED LINE
saveLoc_async(res, nextLoc);
retry();
}
});
});
}
}
SO where should retry(); go because right now with the save sometimes it puts the second location with one of the first items url? why would that happen?
I did something similar to this for an animation.
var actions = [drawXXX, fadeOutYYY, drawXYZ];
this.startAnimation = function () {
actions.reduce(function (previousAction, nextAction) {
return previousAction.then(nextAction)
}, $.when());
}
Your code fires both urls immediately, and does not wait in-between.
What you would have to do is to remove the first url from the array and fire it. In the 'then' branch check if you still have url's in the array and repeat.
Like this (untested, edited to make the code clean again):
var xmlReader = require('cloud/xmlreader.js');
function readResponse_async(xlmString) {
xmlReader.read(xlmString, function (err, res) {
if(err) {
// show an error
} else {
readFirstUrl();
}
});
}
function readFirstUrl() {
if (urlsUnion.length == 0) {
return;
}
var url = urlsUnion.pop();
Parse.Cloud.httpRequest({
url: url,
}).then(function(httpResponse) {
readResponse_async(httpResponse.text);
});
}
readFirstUrl();
Not sure I understand your use of unionUrls array, but if you have your URL's in a urls array, I think this is pretty clean:
function getUrl(url) {
return Parse.Cloud.httpRequest(url)
.then( function(httpResponse) {
return readResponse_async(httpResponse.text);
});
}
urls.reduce( function(prev, url) {
return prev ? prev.then( function() { getUrl(url); }) : getUrl(url);
}, null);

Nodejs - SolrClient, how to wait for response

I got a question regarding the solr-client module of nodejs. I'm using this module for querying against a solr-index.
The module itself works fine as long as I don't have to wait for finishing of the query and as long I need the result only as a async result.
But currently I cannot find out, how I will be able to await the finishing of a search request and use the result in a sequential way.
I have the follwing method in my manager
SolrManager.prototype.promisedQuery = function(query, callback) {
var solrClient = solr.createClient(this.configuration.cores.page);
var docs = null;
var finished = false;
var deferred = Q.defer();
var request = solrClient.search(query, function(err,obj){
if (!err) {
if (obj.response.numFound > 0) {
deferred.resolve(obj.response.docs);
} else {
deferred.resolve(null);
}
} else {
deferred.reject(err);
}
});
var records = null;
var promise = deferred.promise;
promise.then(function(result) {
records = result;
}).fail(function(error){
records = error;
});
return records;
};
The problem here is, that I try to wait for the result of the query and use it as return value of "promisedQuery".
I try since days to use this method in a sequential call, also with different additional modules like "wait.for", "q", etc. but nothing seems to work.
The callback function of the solr-client will always be executed after the manager-method has already returned. Also the promise-methods will be even called after the return from the manager-method.
Can someone help me out on that topic or have some tips, how I can await the response of the solr-client-search operation and then give it back in a sequential way?
Thanks for any help.
Udo Gerhards
over one week, it seems now that I have found a solution:
SolrManager.prototype.promisedQuery = function(query, callback) {
var solrClient = solr.createClient(this.configuration.cores.page);
var docs = null;
var deferred = Q.defer();
var request = solrClient.search(query, function(err,obj){
if (!err) {
if (obj.response.numFound > 0) {
deferred.resolve(obj.response.docs);
} else {
deferred.resolve(null);
}
} else {
deferred.reject(err);
}
});
return deferred.promise;
};
in all other managers, which are calling the above function:
...
var dbPromise = this.solrManager.promisedQuery(query);
var _self = this;
return Q.async(function*(){
var result = yield dbPromise;
return result;
});
...
After first tests, it seems that synchronized methods will wait until the promise is settled.
The only thing is, that it runs only with NodeJs version 0.11.10, which supports generator functions, with activated --harmony-flag and "q"-module.
Best regards
Udo
You are just using the promises a bit incorrectly. Instead of returning records, you need to return 'deferred.promise'. It should look something like this (note that you don't need the callback you passed into promisedQuery).
SolrManager.prototype.promisedQuery = function(query) {
var solrClient = solr.createClient(this.configuration.cores.page),
deferred = Q.defer();
solrClient.search(query, function(err,obj){
if (!err) {
if (obj.response.numFound > 0) {
deferred.resolve(obj.response.docs);
} else {
deferred.resolve(null);
}
} else {
deferred.reject(err);
}
});
return deferred.promise;
};
To use it you would do something like:
SolrManager.promisedQuery(myquery)
.then(function (data) {
// data is whatever your 'resolved' in promisedQuery
}, function (err) {
// err is whatever you rejected in promisedQuery
});
based on rquinns answer I've changed the code like follows:
SolrManager.prototype.promisedQuery = function(query, callback) {
var solrClient = solr.createClient(this.configuration.cores.page);
var docs = null;
var finished = false;
var deferred = Q.defer();
var request = solrClient.search(query, function(err,obj){
if (!err) {
if (obj.response.numFound > 0) {
deferred.resolve(obj.response.docs);
} else {
deferred.resolve(null);
}
} else {
deferred.reject(err);
}
});
return deferred.promise;
};
...
DemoObject.prototype.toString = function() {
return SolrManager.promisedQuery(this.query).then(function(result){
return result['title'];
}).fail(function(error){
return error;
});
};
DemoObject.prototype.typeOf = function() {
return SolrManager.promisedQuery(this.query).then(function(result){
return result['title'];
}).fail(function(error){
return error;
});
};
I think, this is the right way to use the "promise"-object. But what happens when i do the follwing:
...
var demoObject = new DemoObject();
demoObject.query = "id:1";
console.log(''+demoObject);
...
or if I use "demoObject" by concatenating it to a string
...
var string = "Some string "+demoObject;
...
In case of the string concatenation, I'm currently not sure that the string will contain also the title field from the database. Same for console output.
Will nodejs be so intelligent that it resolves for e.g. the string concatenation "after" the results from the database will be available?
BR
Udo

Changing asynchronous function to synchronous function using Pattern promise jquery

I'm trying to use the best practics with Javascript but it's so hard for me.In this case I wanna use some function "avoid" the callbacks. I'm trying to use the Deferred object of jquery to do it. So I believe that I don't how to works fine.
I'm working with phonegap + emberjs + cordova-sqlite-plugin.
I've the follwing function that implements callbacks.
getPuntos: function(callback)
{
var db = window.sqlitePlugin.openDatabase("Rondas", "1.0", "Dev", -1);
var ret ={data:false};
db.transaction(function(tx)
{
tx.executeSql("SELECT * from Punto;",[], function(tx, res)
{
if( res.rows.length !== 0)
{
var puntos = [];
for( var i=0; i<res.rows.length; i++)
{
var descripcion = res.rows.item(i).descripcion;
var id = res.rows.item(i).id;
//console.log( descripcion );
var punto = App.Punto.create( { index: i, id:id, descripcion: descripcion});
puntos.push( punto );
}
ret.data = puntos;
callback(ret.data);
}
});
},function(tx,err){
console.log('Error SQL'+err);
},function(){
console.log( 'Base de datos abierta' );
});
}
and I use this as well:
this.getPuntos(function(puntos){
for( var i=0; i<puntos.length; i++){
console.log( puntos[i] );
}
});
Well, but I try to implements something very clear and easy like:
//whitout use a callback.
var puntos = this.getPuntos();
for( var i=0; i<puntos.length; i++){
console.log( puntos[i] );
}
Then I try to do it:
Change the function getPuntos to _getPuntos, this function has a deferred obeject.
the function getPuntos is wrapper to handle the result of promise getting in the method _getPuntos and try to return.(but not works)
_getPuntos: function()
{
var db = window.sqlitePlugin.openDatabase("Rondas", "1.0", "Dev", -1);
var deferred = jQuery.Deferred();
var ret ={data:false};
db.transaction(function(tx)
{
tx.executeSql("SELECT * from Punto;",[], function(tx, res)
{
if( res.rows.length !== 0)
{
var puntos = [];
for( var i=0; i<res.rows.length; i++)
{
var descripcion = res.rows.item(i).descripcion;
var id = res.rows.item(i).id;
//console.log( descripcion );
var punto = App.Punto.create( { index: i, id:id, descripcion: descripcion});
puntos.push( punto );
}
//ret.data = puntos;
//callback(ret.data);
deferred.resolve(puntos);
}
});
},function(tx,err){
console.log('Error SQL'+err);
},function(){
console.log( 'Base de datos abierta' );
});
return deferred.promise();
},
The wrapper:
getPuntos: function()
{
var promise = this._getPuntos();
var ret = {data:false};
promise.done(function(result)
{
ret.data = result;
return ret.data;
});
while(ret.data ===false ){} //wait for the result until it's available
return ret.data;
},
In the main function I call it :
var puntos = this.getPuntos();
console.log( puntos+"--shoulbe [object]"); //I get Undefined or false, but no my array.
So, is there some way to do that I wanna, convert a asynchronous function in synchronous
function?.
Thanks for all your answers.
The main problem here is that your getPuntos() function in the second example does not return anything hence the null/false value.
getPuntos: function()
{
var promise = this._getPuntos();
var ret = {data:false};
promise.done(function(result)
{
// This happens in the future
ret.data = result;
return ret.data;
});
// This happens now and there is nothing returned
},
When your promise is completed (promise.done) it returns ret.data but this is returned to the promise and is ignored. It is in the wrong scope. You thinking in a traditional 'pull' model where data is pulled towards you and the program blocks until data is available. Javascript uses callbacks and is more of a 'push' model where data is pushed onto callbacks when it is available and there is no blocking.
You need to rearrange things. For example the getPuntos function should return the promise.
getPuntos: function()
{
var promise = this._getPuntos();
return promise;
},
And the main function should add a callback for when the promise is fulfilled.
var puntos = this.getPuntos();
puntos.done(function(result) {
console.log( result+"--shoulbe [object]");
});
Although the getPuntos() function is a bit redundant here as it is just returning the value of _getPuntos().
The problem is that your getPuntos() function sets up the promise.done action, but the returned value never gets passed to the rest of your program. As NoxHarmonium said, your getPuntos() method is superfluous and should be combined with _getPuntos(), but you probably want something like the following (as you can see in this jsFiddle):
PuntosPromise = function() {
this.result = null;
this._getPuntos = function() {
var deferred = $.Deferred();
setTimeout(function(scope) {
scope.result = "It works."
deferred.resolve();
}, 500, this);
return deferred.promise();
}
this.getPuntos = function()
{
var promise = this._getPuntos();
return promise;
}
this.getResults = function() {
return this.result;
};
}
$("#tester").click(function() {
var test = new PuntosPromise();
$.when(test.getPuntos()).then(function() {
alert(test.getResults());
});
});
EDIT:
See #Bergi's modifications given below. While this code works, his solution is cleaner.

Categories

Resources