jQuery Promise Callback - javascript

After having to change a far bit of my callback syntax in order to accommodate firefox limitations I am running into some weird issues.
Markup + Executing Code
function List_Add() {
SP.SOD.executeFunc('SP.js', 'SP.ClientContext', function() {
var listTitle = 'Quote';
var propertiesToAdd = [];
propertiesToAdd.push({
ID: "Q_ID",
newval: 1,
});
addListItems(listTitle, propertiesToAdd)
.done(function(items) {
//Do Heaps of Stuff
})
.fail(function(error) {
console.log(error.get_message());
});
});
}
And the function that is being called to execute this
function addListItems(listTitle, propertiesToAdd) {
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var list = web.get_lists().getByTitle(listTitle);
var listItemCreationInfo = new SP.ListItemCreationInformation();
var newItem = list.addItem(listItemCreationInfo);
propertiesToAdd.forEach(function(entry) {
newItem.set_item(entry.ID, entry.newval);
});
newItem.update();
var d = $.Deferred();
ctx.executeQueryAsync(function() {
d.resolve(true);
return d.promise();
},
function(sender, args) {
d.reject(args);
return d.promise();
});
}
The return d.promise while normally outside the functions was causing timing issues with async execution.
The error I am receiving after running this code is thrown the the mark up + execution
Uncaught TypeError: Cannot read property 'done' of undefined
The values are adding to the lists correctly, so the bulk is working, the .done is not being returned though hence not allowing the execution of the follow up code.

Your return d.promise(); needs to go in the outer function, not the inner callbacks. Whatever values return from the inner asynchronously executed callbacks cannot affect the return value of the outer function.
function addListItems(listTitle, propertiesToAdd) {
// ...
var d = $.Deferred();
ctx.executeQueryAsync(function() {
d.resolve(true);
},
function(sender, args) {
d.reject(args);
}
);
return d.promise();
}

Related

javascript: correctly get the value of the return statement that is coming from a functioncall of a different class

Here is my code:
//in main-class
var mapper = new ImageMapper();
console.log("Snowfall");
var tiles = mapper.cropImage("../../grafiken/webseite/Snowflakes.png");
console.log(tiles);
console.log("ende");
//in ImageMapper-class
cropImage(path) {
this.img.src = path;
var that = this;
this.img.onload = function() {
that.init();
var tiles = that.getTiles();
console.log(tiles);
// that.drawTiles(tiles);
// console.log("in Mapperclass:");
console.log(tiles);
return tiles;
}
}
Browserconsole. Array and other outputs are strangely reverse
As you can see above, the console.log of the returned value gives me undefined.
How can I change that so that I get what I want, which is the value that the function returns inside the class itself (in the functioncall)?
And why does it first call console.log("ende") and after that the array of tiles even though it's in reverse?
function getTiles()
You don't actually return something from inside the cropImage() method and the result from the image.onload() call returns it's results into the void.
A function that does not return anything, gets the value 'undefined'.
So when you log 'tiles', you get the 'undefined' in the console.
So your console.log results are correct.
You probably want the onload function of the image to set a property on the imageMapper instance so you can access it after the image is loaded. And then you have to add a callback ( or promise, or async/await ) to notify the script outside that the mapper has loaded its image.
Do a search for 'how to return from an asynchronous call', since this image loading is basically the same as a manual ajax call for some file.
To have cropImage return a promise can look something like this:
cropImage(path) {
return new Promise(
(resolve,reject)=>{
this.img.src = path;
var that = this;
this.img.onload = function() {
that.init();
var tiles = that.getTiles();
console.log(tiles);
// that.drawTiles(tiles);
// console.log("in Mapperclass:");
console.log(tiles);
resole(tiles);
};
this.img.onerror=reject;
}
)
}
//call it later:
mapper.cropImage("../../grafiken/webseite/Snowflakes.png")
.then(
(tiles)=>console.log('now I have tiles:',tiles)
)
.catch(
(error)=>console.log('there was an error:',error)
)
cropImage actually doesn't return anything. Instead, it "only" contains a event handler, which returns something - but this return value is ignored and got lost.
There are many solutions to your problem. I.e. you could pass a callback function to cropImage which is called inside the load event. The other option would be to return a Promise in cropImage, which handles the callback.
Use a callback:
cropImage(path, finish) {
this.img.src = path;
var that = this;
this.img.onload = function() {
that.init();
var tiles = that.getTiles();
finish(tiles);
}
}
cropImage('...', function(tiles) {
console.log(tiles);
}
second option with a Promise:
cropImage(path, finish) {
return new Promise(function(resolve, reject) {
this.img.src = path;
var that = this;
this.img.onload = function() {
that.init();
var tiles = that.getTiles();
resolve(tiles);
}
});
}
// cropImage with a promise
cropImage('...').then(function(tiles) {
console.log(tiles);
});

javascript - how is functionality Hook order of execution works?

I don't know how to ask this on google that's why I asked here instead,
The console.log from the getListByElement() function won't execute here,
I am modifying a very large existing project and uses functionality hooks for validation purposes and executes that hook on certain .on events, what I want to know is why the console.log won't get executed,
which gets executed first,
Order of execution on my understanding
1. trigger event function for the field `fieldName`
2. fieldName.functionalityHook = [Apple.functionalityHook()];
3. Apple.functionalityHook = function(func) {
4. return function(e) {
5. getListByElement(ele); and display console.log();
6. return func;
Here is the sample code that I have,
var Apple= window.Apple; // global
fieldName.functionalityHook = [Apple.functionalityHook()];
Apple.functionalityHook = function(func) {
return function(e) {
var ele = $(e.target);
getListByElement(ele);
return func;
}
}
function getListByElement(ele){
console.log('ele here');
}
Thank You for answering,
as par my understanding your getListByElement() is not invoking because of the function initialization. You are calling the functionalityHook() before its initialization.
fieldName.functionalityHook = [Apple.functionalityHook()];
Apple.functionalityHook = function(func) {..........
and this invocation returning a function
return function(e) {
var ele = $(e.target);
getListByElement(ele);
return func;
}
and inside this function getListByElement() is calling.
So, the correct code arrangement should be like this.
var Apple= window.Apple;
function getListByElement(ele){
console.log('ele here');
}
Apple.functionalityHook = function(func) {
return function(e) {
var ele = $(e.target);
getListByElement(ele);
return func;
}
}
fieldName.functionalityHook = [Apple.functionalityHook()];

How can I fire JQuery functions sequentially with deferred()?

I am really having trouble wrapping my head around the deferred() method inside jquery. I've spent several hours reading the documentation, but I still don't fully understand what I'm doing.
My basic problem is, I have a series of functions (not ajax calls) that I want to run in sequence, but stop all processes if there is an error in any of them.
Here is how far I've gotten (I've stripped out a bunch of unneeded code and just left the basic idea)
//The module var myModule = (function() {
//Defaults
var vOne;
var VTwo;
var deferred = $.Deferred();
//Private method
var _myPrivateFunction1 = function(userID) {
if(userID >= 10) {
//All is good, set var vOne to true and run next function
vOne = true;
return deferred.promise();
} else {
//User is not allowed, stop everything else and show the error message
return deferred.reject();
}
}
var _myPrivateFunction2 = function() {
if(vOne === true) {
//Ok we can keep going
return deferred.promise();
} else {
//again stop everything and throw error
return deferred.reject();
}
};
var _myPrivateFunction3 = function(element) {
//...and so on
};
var _errorMsgFunction = function(msg) {
$.log("There was an error: " + msg);
return false;
};
//Public method
var myPublicFunction = function(element,call) {
//element is jquery object passed through user "click" event
var userID = element.data('id')
var userAction = element.data('action');
//Now... i want to fire _myPrivateFunction1, _myPrivateFunction2, _myPrivateFunction3 in sequence and STOP all processes, and run
// _errorMsgFunction if there is an error in any of them.
//This is how far I've gotten...
_myPrivateFunction1(userID).then(_myPrivateFunction2(userAction), _errorMsgFunction("Error in _myPrivateFunction2")).then(_myPrivateFunction3(element),_errorMsgFunction("Error in _myPrivateFunction3")).fail(_errorMsgFunction("Error in _myPrivateFunction1"));
};
// Public API
return {
myPublicFunction: myPublicFunction
};
})();
So right now I keep getting "Error in _myPrivateFunction2" (I'm forcing this error for testing purposes), but the other functions after continue to fire...They don't stop. What am I missing here?
You cannot share deferred objects. You should create a different promise from a deferred for each function.
Here is some very simple example, using sycnhronus functions for the sake of simplicity, although promises are meant to be used with asynchronous functions:
var func1 = function(arg){
var dfd = jQuery.Deferred();
if (arg === 0) {
dfd.resolve('func1 Ok');
} else {
dfd.reject('func1 arg != 0');
}
return dfd.promise();
}
var func2 = function(arg){
var dfd = jQuery.Deferred();
if (arg === 0) {
dfd.resolve('func2 Ok');
} else {
dfd.reject('func2 arg != 0');
}
return dfd.promise();
}
var func3 = function(arg){
var dfd = jQuery.Deferred();
if (arg === 0) {
dfd.resolve('func3 Ok');
} else {
dfd.reject('func3 arg != 0');
}
return dfd.promise();
}
If the functions does not depend on other to do their processing, we can do it in parallel using jQuery.when
// Parallel processing
jQuery.when(func1(1), func2(0), func3(0)).then(function(res1, res2, res3){
console.log(res1, res2, res3);
}).fail(function(reason){
console.error(reason); // will fail with reason func1 arg != 0
});
If it is a sequence processing (as I undertood your problem is), you should do:
// Sequential processing
func1(0).then(function(res1){
return func2(res1);
}).then(function(res2){
return func3(res2);
}).then(function(res3){
// everything ran ok, so do what you have to do...
}).fail(function(reason){
console.log(reason);
});
The code above will fail with reason:
> func2 arg != 0
If you have mixed parallel and sequential processing to do, then you should mix both approaches.
Disclaimer
As in my example, if func1 or func2 have side effects, they will not be undone within fail() by themselves.
The best practice is to only have side effects when you are absolutely sure that everything went ok, that is, inside the last then() call.
You will need a separate $.deferred() inside each of your functions, because you want to return unique promise for each function.
//Private method
var _myPrivateFunction1 = function(userID) {
var deferred = $.Deferred();
if(userID >= 10) {
//All is good, set var vOne to true and run next function
vOne = true;
deferred.resolve();
} else {
//User is not allowed, stop everything else and show the error message
deferred.reject();
}
return deferred.promise();
}
Then your public function should work.

Getting correct data when using multiple deferred ajax calls

I have a function that uses two ajax calls in order to get the proper information:
var getUsers = function() {
return $.getJSON("http://codepen.io/chriscoyier/pen/EAIJj.js", function(foo) {
return $.getJSON("http://codepen.io/chriscoyier/pen/EAIJj.js", function(bar) {
return foo['age'] = bar.type;
});
});
}
And an outside function that calls the current function and only continues when the calls are finished.
getUsers().then(function(result) {
// ...
});
Now the weird thing is that if I display the result, the 'age' will show up in the console, but if I try to access it using result['age'], it will return undefined.
Is there a proper way of handling multiple deferred calls?
Code
http://codepen.io/norbiu/pen/bNRQxL
Edit Instead of using a separate deferred, you can chain the ones returned from getJSON() like this
var getUsers = function() {
var foo;
return $.getJSON("http://codepen.io/chriscoyier/pen/EAIJj.js")
.then(function(data) {
foo = data;
return $.getJSON("http://codepen.io/chriscoyier/pen/EAIJj.js")
}).then(function(bar) {
foo['age'] = bar.type;
return foo;
});
}
Note: you need to save the return value from the first call or it won't be accessible to the second.
Original code for posterity
You can use a jQuery Deferred object and return that instead
var getUsers = function() {
var dfd = $.Deferred();
$.getJSON("http://codepen.io/chriscoyier/pen/EAIJj.js")
.done(function(foo) {
$.getJSON("http://codepen.io/chriscoyier/pen/EAIJj.js")
.done(function(bar) {
foo['age'] = bar.type;
dfd.resolve(foo);
}).fail(function(e) {
dfd.reject(e);
})
}).fail(function(e) {
dfd.reject(e);
});
return dfd.promise();
}
http://codepen.io/anon/pen/pvwqZo
The deferred object won't resolve until both requests succeed (and will fail if any of them fail).

How to use jQuery's deferred object with custom javascript objects?

I have a standard javascript object whose prototype is extended with a .start() method taking 2 callbacks as arguments: success and failure respectively. This method performs some asynchronous processing (it's not AJAX) and depending on the result of this processing it invokes either the success or the failure callbacks.
Here's how this could be schematized:
function MyObject() {
}
MyObject.prototype.start = function(successCallback, errorCallback) {
(function(s, e) {
window.setTimeout(function() {
if (Math.random() < 0.8) {
s();
} else {
e();
}
}, 2000);
})(successCallback, errorCallback);
}
It's not really important the exact processing performed inside the method, only that it is asynchronous and non-blocking. I have no control over the point of time when the start method will finish the processing. I also have no control over the prototype and implementation of this method.
What I have control over is the success and failure callbacks. It is up to me to provide them.
Now I have an array of those objects:
var arr = [ new MyObject(), new MyObject(), new MyObject() ];
The order of elements in this array is important. I need to trigger the .start() method on each element of the array consecutively but only once the previous has completed (i.e. the success callback was called). And if an error occurs (the failure callback is called) I want to stop the execution and no longer invoke the .start method on the remaining elements of the array.
I could implement this naively by using a recursive function:
function doProcessing(array, index) {
array[index++].start(function() {
console.log('finished processing the ' + index + ' element');
if (index < array.length) {
doProcessing(array, index);
}
}, function() {
console.log('some error ocurred');
});
}
doProcessing(arr, 0);
This works fine but looking at the jQuery's deferred Object that was introduced in jQuery 1.5 I think that there is a room for improvement of this code. Unfortunately I don't feel very comfortable yet with it and I am trying to learn it.
So my question is is it possible to adapt my naive code and take advantage of this new API and if yes, could you provide me with some pointers?
Here's a jsfiddle with my implementation.
You could do something like this: (jsFiddle)
function MyObject() {
}
MyObject.prototype.start = function(queue) {
var deferred = $.Deferred();
//only execute this when everything else in the queue has finished and succeeded
$.when.apply(jQuery,queue).done(function() {
window.setTimeout(function() {
if (Math.random() < 0.8) {
deferred.resolve();
} else {
deferred.reject();
}
}, 2000);
});
return deferred;
}
var arr = [ new MyObject(), new MyObject(), new MyObject() ];
var queue = new Array();
$.each(arr, function(index, value) {
queue.push(value.start(queue)
.done(function() {
console.log('succeeded ' + index);
})
.fail(function() {
console.log('failed ' + index);
}));
});
Not quite sure wether you would consider this an improvement, though.
When we program, to remember the GRASP principles or guidelines is very important.
http://en.wikipedia.org/wiki/GRASP_(object-oriented_design)
To get High Cohesion and Low Coupling means that our code will be better, more reusable and easier to maintain.
So, the class MyObject mustn't known the queue existance. MyObject will know its own features and methods and anything more.
// Class MyObject
function MyObject(name) {
this.name = name;
}
MyObject.prototype.start = function() {
var deferred = $.Deferred();
var self = this;
setTimeout(function() {
if (Math.random() <= 0.8) {
console.log(self.name + "... ok");
deferred.resolve();
} else {
console.log(self.name + "... fail");
deferred.reject();
}
}, 1000);
return deferred.promise();
}
The main/caller function will know MyObject existance and it will create three instances that they will be executed sequentially.
// Create array of instances
var objectArray = [ new MyObject("A"), new MyObject("B"), new MyObject("C") ];
// Create array of functions to call start function
var functionArray = [];
$.each(objectArray, function(i, obj) {
functionArray.push(
function() {
return obj.start();
}
);
});
// Chain three start calls
$.iterativeWhen(functionArray[0], functionArray[1], functionArray[2])
.done(function() {
console.log("First: Global success");
// Chain three start calls using array
$.iterativeWhen.apply($, functionArray)
.done(function() {
console.log("Second: Global success");
})
.fail(function() {
console.log("Second: Global fail");
});
})
.fail(function() {
console.log("First: Global fail");
});
I have built a plugin for jQuery: iterativeWhen. It works with jQuery 1.8 and later versions.
$.iterativeWhen = function () {
var deferred = $.Deferred();
var promise = deferred.promise();
$.each(arguments, function(i, obj) {
promise = promise.then(function() {
return obj();
});
});
deferred.resolve();
return promise;
};
Jsfiddle here: http://jsfiddle.net/WMBfv/
There's nothing wrong with your implementation. And as we all know, using jQuery isn't always the best method.
I'd do it like this: (without the need to modify the MyObject class..)
function doProcessing(array, index) {
var defer = new $.Deferred();
$.when(defer).then(doProcessing);
array[index++].start(function() {
log('finished processing the ' + index + ' element');
if (index < array.length) {
defer.resolve(array, index);
}
}, function() {
log('some error ocurred => interrupting the process');
});
};
As you can see, there's no real advantage over the plain JavaScript method. :)
Here's my fiddle: http://jsfiddle.net/jwa91/EbWDQ/

Categories

Resources