I want to update multiple rows in a class in Parse. I need to add a new field using "set". I tried saveAll and Promises in parallel to update, but these both are asynchronous. So they consume lot of resources and bandwidth.
How can I do that in a synchronous way. It would be better for me if you can answer using promises in series
Here is the code I'm using currently.But I need in a series way
Parse.Cloud.define("Updating",function(request,response){
var query = new Parse.Query("FollowUp");
query.find({
success: function(results){
for (var i = 0; i < results.length; i++) {
results[i].set("testfield","sometext");
}
Parse.Object.saveAll(results,{
success: function(list){
response.success("ok");
},
error: function(error){
response.error("failed");
}
});
},
error: function(error) {}
});
});
This code is working fine and it is synchronous.
Parse.Cloud.define("Updating",function(request,response){
var query = new Parse.Query("FollowUp");
query.find().then(function(results) {
var promise = Parse.Promise.as();
_.each(results, function(result) {
// For each item, extend the promise with a function to save it.
result.set("newfield","somevalue");
promise = promise.then(function() {
// Return a promise that will be resolved when the save is finished.
return result.save();
});
});
return promise;
}).then(function() {
response.success("working!!");
// Every object is updated.
});
});
or you can even use "for" loop instead of _.each
Parse.Cloud.define("Updating",function(request,response){
var query = new Parse.Query("FollowUp");
query.find().then(function(results) {
var promise = Parse.Promise.as();
for(var i=0;i<results.length;i++){
results[i].set("newfield","somevalue");
promise = promise.then(function() {
// Return a promise that will be resolved when the save is finished.
return results[i].save();
});
});
return promise;
}).then(function() {
response.success("working!!");
// Every object is updated.
});
});
Related
I have this code:
var services = ['EC2', 'S3', 'RDS', 'IAM']
var promises = [];
for(var i = 0; i < services.length; i++) {
var promise = awsStatus('us-east-1', services[i])
promises.push(promise);
console.log(promises);
}
q.all(promises).then(function(data){
console.log(promises);
});
it's supposed to loop on the services array with the awsStatus method. The problem is that sometimes I get all the results I want:
{ service: 'IAM', status: 0 }
{ service: 'EC2', status: 0 }
{ service: 'RDS', status: 0 }
But sometimes I get incomplete results. I thought I needed .then after awsStatus but that also didn't resolve this. What else is wrong in this piece of code?
since now there's a comment, I've also tried this :
var services = ['EC2', 'S3', 'RDS', 'IAM']
var promises = [];
for(var i = 0; i < services.length; i++) {
var promise = awsStatus('us-east-1', services[i]).then(function(promise){
promises.push(promise);
//console.log(promises);
});
}
q.all(promises).then(function(data){
console.log(promises);
});
and it produces the same results.
Try using .each
Promise.each(services, function(service) {
return awsStatus('us-east-1', service).then(function(promise){
promises.push(promise);
//console.log(promises);
});
}).then(function() {
console.log('Done with all instances');
});
I can suggest a solution using async library as follows:
var services = ['EC2', 'S3', 'RDS', 'IAM']
async.map(services,
function(item, done){
awsStatus('us-east-1', item).then(
function(){
done(null, "Completed item " + item);
},
function(err){
done(err + " in item " + item, null);
}
);
},
function(err, results){
if(err) return console.log(err);
console.log(results);
}
);
It will fail and stop executing if one of the promises returns an error. If dont want to stop you can return null in the error function of promise instead of error message as the first argument of done method
There are two problems in your code:
Using q.all() does not guarantee that the code will wait for all the promises to resolve. If one of them is rejected, the then method would be called, not waiting for the rest of the promises. You can solve that by using q.allResolved() instead.
You are logging the wrong objects. You should be logging the results of the promises, and not the promise objects themselves.
Example of how to solve both problems:
q.allResolved(promises).then(function(results) {
console.log(results);
}).catch(function(err) {
console.error(err);
});
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.
I want to chain 4 functions in a Promise chain like so:
function1 -> function2 -> function3 -> function4
My Promise chain
if ($location.$$url !== "/dashboard") {
vm.customURL = true;
// (1) Set root vars & Rebuild tickerTagsContainer:
var promise = TagFactory.buildUrlObject($location.$$url).then(function() {
console.log('TagFactory.buildUrlObject PROMISE returned');
}).then(function() {
console.log('(2) Re-display tags in viewHeader');
// (2) Re-display tags in viewHeader:
viewHeader = ScopeFactory.getScope('viewHeader');
viewHeader.vh.displayViewHeaderTags().then(function() {
console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
});
}).then(function() {
// (3) Reselect timeSpan:
console.log('(3) Reselect timeSpan');
viewHeader.vh.toggleTimeSpan(vm.timeSpan);
// (4) Refresh URL:
console.log('(4) Refresh URL');
ViewFactory.remakeViewObject($location.$$url);
});
}
The resulting console.logs:
^ Note I never see this log:
viewHeader.vh.displayViewHeaderTags().then(function() {
console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
});
Ideally I want to place my (3) function inside that, then chain my (4) like so:
viewHeader.vh.displayViewHeaderTags().then(function() {
console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
console.log('(3) Reselect timeSpan');
viewHeader.vh.toggleTimeSpan(vm.timeSpan).then(function() {
console.log('(4) Refresh URL');
ViewFactory.remakeViewObject($location.$$url);
});
});
However I never see the console.log from the .then function for displayViewHeaderTags
Here is what my displayViewHeaderTags looks like:
function displayViewHeaderTags() {
vm.viewTickerTags = [];
vm.viewTickerTags = TagFactory.retrieveTickerTags('all');
var deferred = $q.defer();
var tikObjs = vm.viewTickerTags.map(function(el) { return el.ticker; });
var tagObjs = vm.viewTickerTags.map(function(el) { return el.tags; });
var tags = _.flatten(tagObjs);
// forEach loops up to 3 times:
tags.forEach(function(tag, i) {
vm.viewTags = [];
ApiFactory.getTagDataSilm(tag.term_id).then(function(data) {
vm.viewTags.push(data.data.ticker_tag);
if (i === tags.length) {
deferred.resolve();
}
});
});
return deferred.promise;
}
Inside my displayViewHeaderTags function I hit a loop which will run up to 3 times, after it's done getting data, it will fill up and array then calls deffered.resolve. then returns it return deferred.promise;
So why do I never see this log? console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
Your i is never the same as the length, because the i variable starts at zero (array indexes start at zero). Which means the if you have an array with length = 2, your i values will be 0 and 1 respectively. It will never equal to zero. Basically, you would want the condition to be:
vm.viewTags.push(data.data.ticker_tag);
if (i + 1 === tags.length) {
deferred.resolve();
}
Anyway, using defer() is a code smell.
A more elegant way of doing it would be using $q.all
var allPromises = [];
var promise;
tags.forEach(function(tag) {
vm.viewTags = [];
promise = ApiFactory.getTagDataSilm(tag.term_id).then(function(data) {
vm.viewTags.push(data.data.ticker_tag);
});
// Create an array of promises, one promise for each request
allPromises.push( promise );
});
// Return a new promise that will only be resolved
// when all the promises of the array `allPromises` are resolved,
// or is rejected when one of them is.
return $q.all( allPromises );
Your chain is not really doing anything since you're not returning a promise from any of those anonymous functions. You're not seeing that log probably because ApiFactory.getTagDataSilm is failing or never resolving. Try adding an error handler into your flow.
if ($location.$$url !== "/dashboard") {
vm.customURL = true;
// (1) Set root vars & Rebuild tickerTagsContainer:
var promise = TagFactory.buildUrlObject($location.$$url).then(function() {
console.log('TagFactory.buildUrlObject PROMISE returned');
}).then(function() {
console.log('(2) Re-display tags in viewHeader');
// (2) Re-display tags in viewHeader:
viewHeader = ScopeFactory.getScope('viewHeader');
return viewHeader.vh.displayViewHeaderTags().then(function() {
console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
});
}).then(function() {
// (3) Reselect timeSpan:
console.log('(3) Reselect timeSpan');
return viewHeader.vh.toggleTimeSpan(vm.timeSpan);
}).then(function() {
// (4) Refresh URL:
console.log('(4) Refresh URL');
return ViewFactory.remakeViewObject($location.$$url);
}).catch(function(error) {
console.log('Something failed', error);
});
}
Within displayViewHeaderTags, you can use $q.all, so that rejections are handled for you:
// forEach loops up to 3 times:
vm.viewTags = [];
return $q.all(_.map(tags, function(tag) {
return ApiFactory.getTagDataSilm(tag.term_id).then(function(data) {
vm.viewTags.push(data.data.ticker_tag);
});
}));
I have this code in a factory:
getAyahsByJuz: function (juzIndex) {
var response = [];
var promises = [];
var self = this;
var deferred = $q.defer();
$timeout(function () {
$http.get('data/quran.json').success(function (data) {
var ayahs = Quran.ayah.listFromJuz(juzIndex);
angular.forEach(ayahs, function (value, key) {
var promise = self.getVerse(value.surah, value.ayah).then(function (res) {
var verse = {
surah: value.surah,
ayah: value.ayah,
text: res
};
response.push(verse);
}, function (err) {
console.log(err);
});
promises.push(promise);
});
});
}, 30);
$q.all(promises).then(function() {
deferred.resolve(response);
});
return deferred.promise;
},
Please note that everything is working fine the verse object is returning properly. However, when I use this in a controller using .then(res). res returns [] instead of the array filled with the verse objects.
Can anyone point out why? Thanks!
The short answer is because your $q.all runs before $timeout & before the $http embedded in $timeout. Let's boil your original code down to its relevant components:
getAyahsByJuz: function (juzIndex) {
var response = [];
var promises = [];
var deferred = $q.defer();
// ...irrelevant stuff that will happen after a $timeout
// this happens IMMEDIATELY (before $timeout):
$q.all(promises).then(function() { // wait for empty promise array
deferred.resolve(response); // resolve with empty response array
}); // side note: this is a broken chain! deferred.promise can't reject
return deferred.promise; // send promise for empty array
}
See the problem? If for some odd reason you need to keep that $timeout, here's the fix with substantial promise refactoring & removing the awful jquery-inspired non-promisy success syntax):
getAyahsByJuz: function (juzIndex) {
var self = this;
// $timeout itself returns a promise which we can post-process using its callback return value
return $timeout(function () {
// returning the $http promise modifies the $timeout promise
return $http.get('data/quran.json').then(function (response) { // you never used this response!
var versePromises = [];
var ayahs = Quran.ayah.listFromJuz(juzIndex);
angular.forEach(ayahs, function (value, key) {
// we'll push all versePromises into an array…
var versePromise = self.getVerse(value.surah, value.ayah).then(function (res) {
// the return value of this `then` modifies `versePromise`
return {
surah: value.surah,
ayah: value.ayah,
text: res
};
});
versePromises.push(versePromise);
});
return $q.all(versePromises); // modifies $http promise — this is our ultimate promised value
// if a versePromise fails, $q.all will fail; add a `catch` when using getAyahsByJuz!
});
}, 30);
}
However, there is still a huge issue here… why aren't you using the server response of your $http call anywhere? What is the point of that first call?
Also I find that $timeout to be extremely suspicious. If you need it then it's likely there's something bad going on elsewhere in the code.
I have some code that will dynamically generate an AJAX request based off a scenario that I'm retrieving via an AJAX request to a server.
The idea is that:
A server provides a "Scenario" for me to generate an AJAX Request.
I generate an AJAX Request based off the Scenario.
I then repeat this process, over and over in a Loop.
I'm doing this with promises here: http://jsfiddle.net/3Lddzp9j/11/
However, I'm trying to edit the code above so I can handle an array of scenarios from the initial AJAX request.
IE:
{
"base": {
"frequency": "5000"
},
"endpoints": [
{
"method": "GET",
"type": "JSON",
"endPoint": "https://api.github.com/users/alvarengarichard",
"queryParams": {
"objectives": "objective1, objective2, objective3"
}
},
{
"method": "GET",
"type": "JSON",
"endPoint": "https://api.github.com/users/dkang",
"queryParams": {
"objectives": "objective1, objective2, objective3"
}
}
]
This seems like it would be straight forward, but the issue seems to be in the "waitForTimeout" function.
I'm unable to figure out how to run multiple promise chains. I have an array of promises in the "deferred" variable, but the chain only continues on the first one--despite being in a for loop.
Could anyone provide insight as to why this is? You can see where this is occuring here: http://jsfiddle.net/3Lddzp9j/10/
The main problems are that :
waitForTimeout isn't passing on all the instructions
even if waitForTimeout was fixed, then callApi isn't written to perform multiple ajax calls.
There's a number of other issues with the code.
you really need some data checking (and associated error handling) to ensure that expected components exist in the data.
mapToInstruction is an unnecessary step - you can map straight from data to ajax options - no need for an intermediate data transform.
waitForTimeout can be greatly simplified to a single promise, resolved by a single timeout.
synchronous functions in a promise chain don't need to return a promise - they can return a result or undefined.
Sticking with jQuery all through, you should end up with something like this :
var App = (function ($) {
// Gets the scenario from the API
// sugar for $.ajax with GET as method - NOTE: this returns a promise
var getScenario = function () {
console.log('Getting scenario ...');
return $.get('http://demo3858327.mockable.io/scenario2');
};
var checkData = function (data) {
if(!data.endpoints || !data.endpoints.length) {
return $.Deferred().reject('no endpoints').promise();
}
data.base = data.base || {};
data.base.frequency = data.base.frequency || 1000;//default value
};
var waitForTimeout = function(data) {
return $.Deferred(function(dfrd) {
setTimeout(function() {
dfrd.resolve(data.endpoints);
}, data.base.frequency);
}).promise();
};
var callApi = function(endpoints) {
console.log('Calling API with given instructions ...');
return $.when.apply(null, endpoints.map(ep) {
return $.ajax({
type: ep.method,
dataType: ep.type,
url: ep.endpoint
}).then(null, function(jqXHR, textStatus, errorThrown) {
return textStatus;
});
}).then(function() {
//convert arguments to an array of results
return $.map(arguments, function(arg) {
return arg[0];
});
});
};
var handleResults = function(results) {
// results is an array of data values/objects returned by the ajax calls.
console.log("Handling data ...");
...
};
// The 'run' method
var run = function() {
getScenario()
.then(checkData)
.then(waitForTimeout)
.then(callApi)
.then(handleResults)
.then(null, function(reason) {
console.error(reason);
})
.then(run);
};
return {
run : run
}
})(jQuery);
App.run();
This will stop on error but could be easily adapted to continue.
I'll try to answer your question using KrisKowal's q since I'm not very proficient with the promises generated by jQuery.
First of all I'm not sure whether you want to solve the array of promises in series or in parallel, in the solution proposed I resolved all of them in parallel :), to solve them in series I'd use Q's reduce
function getScenario() { ... }
function ajaxRequest(instruction) { ... }
function createPromisifiedInstruction(instruction) {
// delay with frequency, not sure why you want to do this :(
return Q.delay(instruction.frequency)
.then(function () {
return this.ajaxRequest(instruction);
});
}
function run() {
getScenario()
.then(function (data) {
var promises = [];
var instruction;
var i;
for (i = 0; i < data.endpoints.length; i += 1) {
instruction = {
method: data.endpoints[i].method,
type: data.endpoints[i].type,
endpoint: data.endpoints[i].endPoint,
frequency: data.base.frequency
};
promises.push(createPromisifiedInstruction(instruction));
}
// alternative Q.allSettled if all the promises don't need to
// be fulfilled (some of them might be rejected)
return Q.all(promises);
})
.then(function (instructionsResults) {
// instructions results is an array with the result of each
// promisified instruction
})
.then(run)
.done();
}
run();
Ok let me explain the solution above:
first of all assume that getScenario gets you the initial json you start with (actually returns a promise which is resolved with the json)
create the structure of each instruction
promisify each instruction, so that each one is actually a promise whose
resolution value will be the promise returned by ajaxRequest
ajaxRequest returns a promise whose resolution value is the result of the request, which also means that createPromisifiedInstruction resolution value will be the resolution value of ajaxRequest
Return a single promise with Q.all, what it actually does is fulfill itself when all the promises it was built with are resolved :), if one of them fails and you actually need to resolve the promise anyways use Q.allSettled
Do whatever you want with the resolution value of all the previous promises, note that instructionResults is an array holding the resolution value of each promise in the order they were declared
Reference: KrisKowal's Q
Try utilizing deferred.notify within setTimeout and Number(settings.frequency) * (1 + key) as setTimeout duration; msg at deferred.notify logged to console at deferred.progress callback , third function argument within .then following timeout
var App = (function ($) {
var getScenario = function () {
console.log("Getting scenario ...");
return $.get("http://demo3858327.mockable.io/scenario2");
};
var mapToInstruction = function (data) {
var res = $.map(data.endpoints, function(settings, key) {
return {
method:settings.method,
type:settings.type,
endpoint:settings.endPoint,
frequency:data.base.frequency
}
});
console.log("Instructions recieved:", res);
return res
};
var waitForTimeout = function(instruction) {
var res = $.when.apply(instruction,
$.map(instruction, function(settings, key) {
return new $.Deferred(function(dfd) {
setTimeout(function() {
dfd.notify("Waiting for "
+ settings.frequency
+ " ms")
.resolve(settings);
}, Number(settings.frequency) * (1 + key));
}).promise()
})
)
.then(function() {
return this
}, function(err) {
console.log("error", err)
}
, function(msg) {
console.log("\r\n" + msg + "\r\nat " + $.now() + "\r\n")
});
return res
};
var callApi = function(instruction) {
console.log("Calling API with given instructions ..."
, instruction);
var res = $.when.apply(instruction,
$.map(instruction, function(request, key) {
return request.then(function(settings) {
return $.ajax({
type: settings.method,
dataType: settings.type,
url: settings.endpoint
});
})
})
)
.then(function(data) {
return $.map(arguments, function(response, key) {
return response[0]
})
})
return res
};
var handleResults = function(data) {
console.log("Handling data ..."
, JSON.stringify(data, null, 4));
return data
};
var run = function() {
getScenario()
.then(mapToInstruction)
.then(waitForTimeout)
.then(callApi)
.then(handleResults)
.then(run);
};
return {
// This will expose only the run method
// but will keep all other functions private
run : run
}
})($);
// ... And start the app
App.run();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
jsfiddle http://jsfiddle.net/3Lddzp9j/13/
You have a return statement in the loop in your waitForTimeout function. This means that the function is going to return after the first iteration of the loop, and that is where you are going wrong.
You're also using the deferred antipattern and are using promises in places where you don't need them. You don't need to return a promise from a then handler unless there's something to await.
The key is that you need to map each of your instructions to a promise. Array#map is perfect for this. And please use a proper promise library, not jQuery promises (edit but if you absolutely must use jQuery promises...):
var App = (function ($) {
// Gets the scenario from the API
// NOTE: this returns a promise
var getScenario = function () {
console.log('Getting scenario ...');
return $.get('http://demo3858327.mockable.io/scenario');
};
// mapToInstructions is basically unnecessary. each instruction does
// not need its own timeout if they're all the same value, and you're not
// reshaping the original values in any significant way
// This wraps the setTimeout into a promise, again
// so we can chain it
var waitForTimeout = function(data) {
var d = $.Deferred();
setTimeout(function () {
d.resolve(data.endpoints);
}, data.base.frequency);
return d.promise();
};
var callApi = function(instruction) {
return $.ajax({
type: instruction.method,
dataType: instruction.type,
url: instruction.endPoint
});
};
// Final step: call the API from the
// provided instructions
var callApis = function(instructions) {
console.log(instructions);
console.log('Calling API with given instructions ...');
return $.when.apply($, instructions.map(callApi));
};
var handleResults = function() {
var data = Array.prototype.slice(arguments);
console.log("Handling data ...");
};
// The 'run' method
var run = function() {
getScenario()
.then(waitForTimeout)
.then(callApis)
.then(handleResults)
.then(run);
};
return {
run : run
}
})($);
App.run();