use Promises in my code ?using deffered object and Jquery - javascript

Hi I am using Jquery and ember to delete certain elements ,I want to use Deferred objects to stop the code and then next statements has to be executed
Here KillMany is Function once it is called it will execute array.forEach(tryKill);
statement
that contains an array of elemets[array contains 100 elements inside for each each time a call back is calling to delete the each element from server]
Here I want to execute my code after completely finishing [deletion of elements] myFinalblock callback has to be calle
please guide me
killMany: function(c) {
var t = this
, wait = []
, dfd = new $.Deferred();
function keep(tile) {
tile.setProperties({ isSelected: false, isHidden: false });
}
function destroy(tile) {
if (t.get('reports')) {
t.get('reports').removeObject(tile.entity);
}
tile.remove.bind(tile);
}
function tryKill(tile) {
tile.set('isHidden', true);
tile.get('entity').kill()
.then(destroy.bind(null, tile),
keep.bind(null, tile));
}
function myFinalblock(){
this.set('selectedTiles', []);
}
this.set('promptDestroyMany', false);
if (c.response) {
var array = this.get('selectedTiles');
array.forEach(tryKill);
myFinalblock();
}
},

You seem to be missing the point of promises a bit. They do not "stop" your code. They allow you to cleanly route your code's asyncrhonous functionality. Therefore you need a way to wait until all of the tryKill calls are done before calling myFinalBlock. To do this, you first need to modify your tryKill function to return its promise:
function tryKill(tile) {
tile.set('isHidden', true);
return tile.get('entity')
.kill()
.then(destroy.bind(null, tile),
keep.bind(null, tile));
}
Then you can do this:
var tiles = this.get('selectedTiles');
$.when.apply($, tiles.map(tryKill))
.then(myFinalBlock)
.done();
On a side note, I suggest looking into a proper promise library and not using jQuery's built-in deferreds, as they are known to have a number of problems.

Related

Asynchronous recursion handling and event delegation in CasperJS

I'm attempting to run an automated job with CasperJS which brings in a JSON file and checks if certain elements on a given page declared in each JSON object exist. I am attempting to write this recursively, but I am running in to an issue where the beginning of the asynchronous recursive flow (i.e. my only synchronous function) is returning synchronously (as expected) and causing the script to fail.
According to this CasperJS github issue, Promises are not currently supported in PhantomJS. I have also tried the .waitFor() CasperJS method, but this causes an infinite loop if I try and bubble up a return value of true from the final asynchronous method (there is also the issue of waitFor being controlled by a timeout value and not waiting on a response, but the timeout value can be changed). Here is a code example attempting to use waitFor below, simplified slightly:
casper.start(urlStart, function() {
recursiveRun(jsonInput.length);
});
var recursiveRun = function(count) {
if (count) {
casper.waitFor(function check() {
return controller(); // Infinite loop here
}, function then() {
jsonInput.shift();
recursiveRun(count - 1);
}
}
}
var controller = function() {
casper.thenOpen(jsonInput[0].url, function() {
return renavigation();
}
}
var renavigation = function() {
casper.waitForSelector("#nav", function() {
this.thenOpen(inventoryUrl, function() {
return inventoryEvaluation();
}
}
}
var inventoryEvaluation = function() {
casper.waitForSelector("#status", function() {
this.evaluate(function() {
// Unimportant in-browser function
}
return true;
}
}
casper.run();
Why is this not working? What is the proper way to recursively and asynchronously perform these actions, if at all?
The CasperJS method .eachThen() seems to perform this functionality as intended.
From the example above, the synchronous function which recursively calls itself can be entirely replaced by having CasperJS iterate over the jsonInput variable in the callback of the .start() method.
casper.start(urlStart, function() {
this.eachThen(jsonInput, function() {
controller();
jsonInput.shift();
}
}

JQuery Promises On Functions Already Containing Callbacks

I'm hoping I can get my problem across.
I have a locally developed JS library called ConfirmDialog. ConfirmDialog has two callbacks: onConfirmYes() and onConfirmNo(). Here's a usage example:
ConfirmDialog.onConfirmYes() = function() {
// make a jQuery ajax call
}
ConfirmDialog.onConfirmNo() = function() {
// make a different ajax call
}
ConfirmationDialog.confirm("Are you sure you wish to do this?");
What I need to do is chain this together using jQuery Deferred/Promise.
var confirmTraining = false;
var confirmNextAvailDate = false;
function confirmTraining() {
ConfirmDialog.onConfirmYes() = function() {
// make a jQuery ajax call to accept training,
// successful ajax call sets confirmTraining
// to true
}
ConfirmDialog.onConfirmNo() = function() {
// make a different ajax call to decline training,
// successful ajax call sets confirmTraining to
// false
}
ConfirmationDialog.confirm("Do you wish to accept training?");
}
function confirmNextAvailableTraining() {
if (confirmTraining == true) {
ConfirmationDialog.onConfirmYes() = function() {
// ajax call to reserve training, success sets
// sets confirm next date to true
}
ConfirmationDialog.onConfirmNo() = function() {
// ajax call to put person on waitlist
}
ConfirmationDialog.confirm("Do you wish for the next available class?");
}
}
// so on
The second method will require access to the boolean value set by the result of an ajax call made by the first method.
So, here's what I need to do:
// wrong syntax, I know
confirmTraining().then(confirmNextAvailableTraining).finally(function() {
// whatever else needs doing
}
The problem I have is that since the variables are not set until the callback fires for the ConfirmDialog, I cannot figure out how to set up the Deferred/Promise for the methods I wish to chain.
Can anyone help?
Jason
Look like you will have to actually use a promise object inconfirmDialog.onConfirmYes() and confirmDialog.onConfirmNo().
ConfirmDialog.onConfirmYes = function() {
//create a promise object that will make the call
// return the promise object
}
ConfirmDialog.onConfirmNo = function() {
//create a promise object that will make the call
// return the promise object
}
function confirmTraining() {
var aPromiseobj;
if(logic to determine what was picked)
aPromiseobj = ConfirmDialog.onConfirmYes();
}else{
aPromiseobj = ConfirmDialog.onConfirmNo()
}
return aPromiseobj;
}
Now when you called then it will be on a promise object
confirmTraining().then(confirmNextAvailableTraining).finally(function() {
// whatever else needs doing
}

getting ".then() is not a function" error when using AngularJS

This is my JS:
self.obj = {}
self.obj.accessErrors = function(data) {
var cerrorMessages = [];
for (prop in data) {
if (data.hasOwnProperty(prop)){
if (data[prop] != null && data[prop].constructor == Object) {
self.obj.fetch[accessErrors](data[prop]);
}
else {
cerrorMessages.push(data[prop]);
}
}
}
return cerrorMessages;
};
self.obj.fetch = {
X: function() {
// do stuff.
},
Y: function(callback) {
$http.get('/yposts/').then(
function(response) {
self.posts = response.data;
callback(self.posts);
},
function(response) {
self.posts = {};
self.obj.accessErrors(response.data).then(function(cerrrorMessages) {
callback(posts, cerrorMessages);
});
}
);
}
};
And I am getting an error pointing to this line:
self.obj.accessErrors(response.data).then(function(cerrrorMessages) {
The error says:
TypeError: self.obj.accessErrors(...).then is not a function
Any idea how to solve this?
self.obj.accessErrors(response.data) does not return a promise so therefore, you can't use promise methods on it.
If you want it to return a promise and you want that promise to reflect when all the fetch() operations are done and those operations are actually async, then you will have to make all your async code into using promises and you will have to combine them all using Promise.all() or the angular equivalent and convert from using callbacks in fetch to just using a promise. Right now, you have a mix which is difficult to program with.
The .then() construction is only needed when using Promise objects - essentially, instead of returning a value, the function returns an object that resolves to a value at some point in the future (which is then passed into the function that you pass to .then().
But you are right in that you need an asynchronous pattern to do this correctly, since fetch.Y is an asynchronous method. A good thing to do would be to create an array of promises at the beginning of your accessErrors function, like so:
var fetchPromises = [];
and then replace self.obj.fetch[accessErrors](data[prop]); with something that calls push on that array to add the promise that fetch returns to it.
Then, instead of returning accessErrors, return Promise.all(fetchPromises).
This will require some fairly significant modification to your code, however - namely, you will need to rewrite it so that it uses the Promise API instead of this callback by itself (which shouldn't be too difficult to do).

Use helper modules for repeated, ordered tasks when functional testing in intern

I'm trying to create a module that will fill in form inputs when functional testing, and I'd like to be able to call it from multiple test suites.
Pseudo code for the helper file (helper.js)
module.exports = {
fillForm: function() {
this.findByCssSelector('#firstname')
.click()
.pressKeys('John')
.end()
},
anotherFunction: function() {
// more code
}
}
In the spec for the functional test, I load that module as helper and I can see it execute. However, it seems I can't use this syntax and guarantee that the chained steps execute in the defined order:
'Test filling form data': function() {
return this.remote
.get(require(toUrl(url))
// should happen first
.then(helper.fillForm)
// should happen second
.then(helper.anotherFunction)
// only after the above should the click happen
.findByCsSelector('#submit')
// click evt should show the #someElement element
.click()
.findByCssSelector('#someElement')
.getComputedStyle('display')
.then(style) {
// assertions here
}
It seems that the promise chaining allows the click event to happen before the then callbacks have executed. Is this sort of flow possible with intern?
UPDATE:
For the moment, working around this with this sort of code:
var remote = initTest.call(this, url);
return helpers.fillForm1Data.call(remote)
.otherChainedMethodsHere()
.moreChainedMethods()
.then() {
// assertion code here
where the initTest method does url fetching, window sizing, clearing data, and the fillForm1Data does as you'd expect. But the syntax is pretty ugly this way.
Your helper is not returning any value so it is treated as a synchronous callback and the next thing in the chain is executed immediately. You also cannot return this from a promise helper or it will cause a deadlock (because the Command promise will be waiting for itself to resolve—Intern will throw an error instead if you try to do this), so you need to create a new Command and return that if you want to use the chained Command interface within your helper:
module.exports = {
fillForm: function() {
return new this.constructor(this.session)
.findByCssSelector('#firstname')
.click()
.pressKeys('John');
},
anotherFunction: function() {
// more code
}
};
You can also just return from this.session instead if you don’t care about the convenience of the Command API and can deal with normal promise callback chains:
module.exports = {
fillForm: function() {
var session = this.session;
return session.findByCssSelector('#firstname')
.then(function (element) {
return element.click();
})
.then(function () {
return session.pressKeys('John');
});
},
anotherFunction: function() {
// more code
}
};

Issue with using jQuery deferred

I am using jQuery Deferred. I want to do 2 things when I click on a link to open modal dialog;
Call my API
Call my template (where values from API would get populated)
So I am doing the following;
self.myModel = new myModel();
self.myModel.url = "api/myModel/";
self.scenarioRecInfoDeferred = new $.Deferred();
self.myModel.fetch({
success : function(){
myModelDeferred.resolve();
}
});
self.tmplDeferred = new $.Deferred();
$.when(self.myModelDeferred, self.tmplDeferred).done(function(modalTemplates) {
});
require(['text!templates/mytemp/my-tpl.html'], function(modalTemplates) {
self.tmplDeferred.resolve(modalTemplates);
});
Now I am not getting anything for "modalTemplates" inside
$.when(self.myModelDeferred, self.tmplDeferred).done(function(modalTemplates) {}
Am I doing something wrong in accessing/passing the parameter?
I believe you need 2 parameters in your .done() function handler. The first parameter will correspond to myModelDeferred arguments (which will be undefined) and the second will correspond to tmplDeferred arguments.
I made a quick jsfiddle to test myself.
var action1 = $.Deferred();
var action2 = $.Deferred();
setTimeout(function(){
action1.resolve();
}, 1000);
setTimeout(function(){
action2.resolve("def", "ghi");
}, 500);
$.when(action1.promise(), action2.promise()).done(function(args, args2){
console.log(args); // undefined
console.log(args2); // ["def", "ghi"]
});
DEMO
Hope this helps!
Why do you need to declare the deferred before loading the template?
You could do whatever you need to do with modalTemplates inside that require block and just skip the tmplDeferred use at all.
require(['text!templates/mytemp/my-tpl.html'], function(modalTemplates) {
self.myModelDeferred.done(function(modalTemplates) {
});
});
Anyway, if you want to stick to that structure, I don't think that's the way to pass a variable to a deferred. You could instead assign it as self.tmplDeferred.modalTemplates=modalTemplates. So, the next should work
$.when(self.myModelDeferred, self.tmplDeferred).done(function(self.tmplDeferred.modalTemplates) {
});
require(['text!templates/mytemp/my-tpl.html'], function(modalTemplates) {
self.tmplDeferred.modalTemplates=modalTemplates;
self.tmplDeferred.resolve();
});

Categories