I'm new to the module pattern and closures in javascript. I'm trying to create a module that gets JSON data so I can display it on my webpage.
var EmailLogs = (function(){
var emailLogs = [];
var config = {};
function init(options){
for(var prop in options) {
if(options.hasOwnProperty(prop)){
config[prop] = options[prop];
}
}
setEmailLogs();
}
function setEmailLogs(){
$.ajax({
type: "POST",
headers : { "cache-control": "no-cache" },
url: "../../ajax/adminGetEmailLogs.php",
data: {options: JSON.stringify(config)},
dataType: 'json',
success: function(values){
if(values.success)
{
emailLogs = values.emailLogs;
}
}
});
}
function getEmailLogs(){
return emailLogs;
}
return{
init: init,
getEmailLogs: getEmailLogs,
}
})();
var options = {
sData : [
'email_id'
],
filters : {
user_id : 44860,
}
}
EmailLogs.init(options);
console.log(EmailLogs.getEmailLogs());
I'm trying to run the ajax call when init is run. Then I'm looking to get the emailLogs variable to display. I presume because my ajax is being run async, that's why I can't get my variables after. How do I make sure setEmailLogs() has ran before I running getEmailLogs().
$.ajax() implements the Promise interface and will return a Deferred object, see the jQuery docs
The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the Promise interface, giving them all the properties, methods, and behavior of a Promise (see Deferred object for more information).
This means that you can use the promise interface to make sure the desired method is run after $.ajax() is done.
Modify your code to something make use of the promise interface. Basically $.ajax() returns to you the promise that it will be executed. You can then chain your next method to execute once the promise is satisfied. You often see patterns like this chaining multiple promises (in psuedo code):
doAjaxRequest()
.then(doSomethingElse)
.then(doAnotherSomethingElse)
This way you can write quite clean async code as oppossed to haven a lot of callbacks invoking eachother.
The documentation for Q promises has some good examples that explain the way promises work.
I modified your snippet to uses promises:
var EmailLogs = (function(){
var emailLogs = [];
var config = {};
function init(options){
for(var prop in options) {
if(options.hasOwnProperty(prop)){
config[prop] = options[prop];
}
}
setEmailLogs().done(function(values) {
if(values.success)
{
console.log(values.emailLogs)
}
});
}
function setEmailLogs(){
return $.ajax({
type: "POST",
headers : { "cache-control": "no-cache" },
url: "../../ajax/adminGetEmailLogs.php",
data: {options: JSON.stringify(config)},
dataType: 'json'
});
}
return{
init: init,
getEmailLogs: getEmailLogs,
}
})();
var options = {
sData : [
'email_id'
],
filters : {
user_id : 44860,
}
}
EmailLogs.init(options);
Related
I've reviewed all the related topics, but for some reason am unable to understand the correct syntax of resolving my case.
It's Javascript on Framework7 platform.
There is a function that has a $.each inside, which cycles through an array of items and executes an async POST operation to update that item.
There's a requirement to update items in two separate locations, so I execute this function twice, each time with a different arguments.
What would be the best approach to chain those two functions together in a sequence to be able to reload the page, as all async requests have been executed?
Thank you in advance!
UPDATE!
I managed to get this far, but promises.then doesn't result into "Success":
var tasks = [1,2,3];
var docs = [3,4,5];
var functions = [asyncOperation(tasks),asyncOperation(docs)]
var asyncOperation = function(items) {
return new Promise(function (resolve, reject) {
var deferreds = [];
items.forEach(function(i,e){
deferreds.push(
app.request({
url: buildurlnew,
type: "POST",
headers: buildheader,
contentType: "application/json;odata=verbose",
data: JSON.stringify(UpdatePayload),
success: function (data) {},
error: function(xhr, textStatus, errorThrown ) {}
)
});
return deferreds;
});
}
var promises = Promise.all(functions);
promises.then(function(results) {
//console.log("Success");
});
UPDATE 2 - The changed code, as per suggestions
var processtasks = function(array, dig) {
var getlistname = GetItemTypeForListName("Alerts")
var itemProperties = {'Title' :"Test"};
var UpdatePayload = {'__metadata': {'type': getlistname}};
for(var prop in itemProperties){
UpdatePayload[prop] = itemProperties[prop]
}
var buildurl = "<REST URL to Sharepoint list>"
var buildheader = { "Accept": "application/json;odata=verbose", "X-RequestDigest" : dig, "X-HTTP-Method": "MERGE", "If-Match": "*"}
return Promise.all(array.map(function(item) {
buildurlnew = buildurl+"("+item+")";
return app.request({
url: buildurlnew,
type: "POST",
headers: buildheader,
contentType: "application/json;odata=verbose",
data: JSON.stringify(UpdatePayload),
success: function (data) {},
error: function(xhr, textStatus, errorThrown) {}
});
}));
}
var processitems = function(listName, array, dig, type, source, web) {
var getlistname = GetItemTypeForListName(listName)
var buildurl = "<REST URL to Sharepoint list>"
var buildheader = { "Accept": "application/json;odata=verbose", "X-RequestDigest" : dig, "If-Match": "*"}
return Promise.all(array.map(function(item) {
var itemProperties = {'UserId' : app.data.UserID, 'Title' : item};
var UpdatePayload = {'__metadata': {'type': getlistname}};
for(var prop in itemProperties){
UpdatePayload[prop] = itemProperties[prop]
}
return app.request({
url: buildurl,
type: "POST",
headers: buildheader,
contentType: "application/json;odata=verbose",
data: JSON.stringify(UpdatePayload),
success: function (data) {},
error: function(xhr, textStatus, errorThrown ) {}
});
}));
}
processitems(listName, array, dig, type, source, web).then(function(r1) {
return processtasks(ids,dig);
}).then(function(r2) {
console.log(r2);
}).catch(function(err) {
console.log(err);
});
First you have to fix your asyncOperation() function because it is not properly returning a promise that is connected to the underlying async operations you're doing.
This code doesn't really make much sense. You have a basic structure of this:
var asyncOperation = function(items) {
return new Promise(function(resolve, reject) {
// body of function here
});
}
So far so good. But, what you need to do inside that promise executor function is start some async operation and call resolve or reject when you're done. You aren't doing that. Therefore, your promise never resolves or rejects.
Instead, you are returning an array of deferreds from the promise executor callback which does nothing. The promise executor function does not expect any return value so returning a value from it does nothing. You have to indicate completion of your asynchronous operation in the promise executor by calling either resolve(...) or reject(...).
If app.request() returns a promise, then you don't even need to make your own promise at all. You can just do something like this:
var asyncOperation = function(items) {
return Promise.all(items.map(function(item) {
return app.request(...);
}));
}
asyncOperation(...).then(function(results) {
// all done here
}).catch(function(err) {
// error here
});
items.map() generates an array of promises and Promise.all() returns a new single promise that monitors that array of promise and will resolve when all the promises in the array resolve or reject when any one of them rejects.
If app.request() does not return a promise, then you probably should make a "promisified" version that does so you can use it with promise functions like Promise.all(), perhaps using util.promisify().
To run two of these in parallel (which seems practical since they don't appear to depend upon one another), you could do this:
Then, once you are properly returning a promise for your function, if you have two of these, you can just use Promise.all() on more than one function call:
Promise.all([asyncOperation(...), asyncOperation(...)]).then(function(results) {
// both done here
// not the result may be an array of arrays
}).catch(function(err) {
// error here
});
What would be the best approach to chain those two functions together in a sequence
To run them in sequence, you can do this:
asyncOperation(args1).then(function(r1) {
return asyncOperation(args2);
}).then(function(r2) {
console.log(r2);
}).catch(function(err) {
console.log(err);
});
I am testing a code using jasmine and creating a mock object for ajax method
spyOn($,'ajax').and.callFake(function(e){
console.log("is hitting");
})
to test the following piece of code
$.ajax({
url: AppManager.defaults.contextPath + "/solutions/mcn/mcn-lookup-list",
data: {
mcnNumber : mcnNumberData,
mcnCustomerName : mcnCustomerNameData
},
dataType: "json",
type: "GET",
global: false
})
.done(function(data) {
solution.CommonObjects.theSolution.orderHandoff.mcnSearchData = self.filterMCNSearchData(data, resultObj);
$promise.resolve();
})
.fail(function() {
$promise.reject();
self.displayErrorPopup('MCN Search Error','There is no MCN associated with MCN Number or MCN Customer Name Entered!!!');
});
},
It's throwing an error cannot read done of undefined . Do I need to create a spy for that also . Please help with the code to do so
Issues with your code:
You are spying it right but you need to send in a promise object back via your spy. Basically you need to return something like this ==> return new $.Deferred().resolve(dummyData).promise();
There are multiple ways to create a deferred object/promise object. I suggest you to read both Promise & Deferred
Also could you explain where your $promise is coming from? is this some feature of require.js?
Below is one way to fake the ajax calls.
var testObj = {
ajaxFunction: function() {
$.ajax({
url: 'https://jsonplaceholder.typicode.com/posts/1'
}).done(function(data){
consoleLogFunction(data);
});
}
};
var consoleLogFunction = function(data){
console.log("The lengh of array is..=> "+ data.length);
};
describe("ajax test suite", function() {
it("expect true to be true", function() {
var dummyData = ["Foo", "Boo"];
spyOn($, 'ajax').and.callFake(function(e){
return new $.Deferred().resolve(dummyData).promise();
});
spyOn(window, 'consoleLogFunction').and.callThrough();
testObj.ajaxFunction();
expect(window.consoleLogFunction).toHaveBeenCalledWith(dummyData);
});
});
I have a method that is called in several places in the project. I've done method. the first method call do Ajax get, cache data in class property and fire callback. Second call method only call callback with cached data. I would like to add the ability to load data synchronously. Date should be returned by the method. I added an additional parameter to call {async: false}, but I wonder if there is a better solution using ES7 promises?
This is my callback solutions.
export class loadData {
constructor() {
this.data = [];
}
getData({callback, async = true}){
let syncData = this.data;
if( this.data.length === 0 ){
$.ajax({
beforeSend: authorizationManager.addAuthorizeHeader(),
url: apiUrl + '/Data/datadata',
dataType: 'json',
cache: true,
async: async
}).done((data)=>{
if(async) callback(data);
this.data = data;
syncData = data;
});
} else {
if(async) callback(this.data);
}
if(async === false) return syncData;
}
}
loadDataTest = new loadData();
call async
loadDataTest.getData({
callback: (data) =>{
console.log(data);
}
});
call sync
let a = loadDataTest.getData({
async: false
});
Promises are almost always the better solution. Of course they are never synchronous, but that's usually the better solution as well. This is how it would look like:
export class loadData {
constructor() {
this.promise = null;
}
getData() {
if (this.promise == null) {
this.promise = Promise.resolve($.ajax({
beforeSend: authorizationManager.addAuthorizeHeader(),
url: apiUrl + '/Data/datadata',
dataType: 'json',
cache: true
}));
}
return this.promise;
}
}
And the call:
loadDataTest.getData().then((data) => {
console.log(data);
});
I would like to add the ability to load data synchronously
I don't think you really want that. If all you want is synchronous-looking syntax for asynchronous functionality, have a look at async/await.
If I have to leverage niceties of jQuery AJAX API and set my own custom settings for each ajax call my app makes like below:
Say I have a page which displays employee information within table by making ajax calls to some API.
define(["jQuery"], function($) {
var infoTable = function (options) {
function init() {
// Provide success callback
options.success_callback = "renderData";
getData();
}
function renderData() {
// This callback function won't be called as it is not
// in global scope and instead $.ajax will try to look
// for function named 'renderData' in global scope.
// How do I pass callbacks defined within requirejs define blocks?
}
function getData() {
$.ajax({
url: options.apiURL,
dataType: options.format,
data: {
format: options.format,
APIKey: options.APIKey,
source: options.source,
sourceData: options.sourceData,
count: options.count,
authMode: options.authMode
},
method: options.method,
jsonpCallback: options.jsonpCallback,
success: options.success_callback,
error: options.error_callback,
timeout: options.timeout
});
}
}
return {
init: init
}
}
How do I achieve this?
I know we can use JSONP request as require calls but that restricts me to using jsonp, making GET requests and all other features $.ajax offers.
This example would let you either use a default success callback, or provide an override, using:
success: options.successCallback || renderData
(The example uses jsfiddle rest URLs - this fact is unimportant, and stripped out the data object to keep the example short)
define("mymodule", ["jquery"], function($) {
function renderData() {
console.log("inside callback");
}
function getData(options) {
$.ajax({
url: options.apiURL,
dataType: options.format,
method: options.method,
jsonpCallback: options.jsonpCallback,
success: options.successCallback || renderData,
error: null,
timeout: options.timeout
});
}
return {
getData: getData
}
});
require(["mymodule"], function(m) {
console.log(m, m.getData({
apiURL: "/echo/json/"
}));
console.log(m, m.getData({
successCallback: function() { console.log("outside callback"); },
apiURL: "/echo/json/"
}));
});
Would print:
GET http://fiddle.jshell.net/echo/json/ 200 OK 263ms
Object { getData=getData()} undefined
GET http://fiddle.jshell.net/echo/json/ 200 OK 160ms
Object { getData=getData()} undefined
inside callback
outside callback
I'm trying to trigger an action after a Javascript object has been created via an AJAX call. My object looks something like this:
function API(uid,accessToken){
$.ajax("path/to/file", {
type: "POST",
data: { user: uid, auth: accessToken },
dataType: "json",
success: function(jsonData) {
arrayname = jsonData[values]
}
});
}
I tried to use JQuery's $.when function to do a callback after the object setup is complete (ie. the array is populated with the ajax response), which looked like this:
$.when( API = new API(uid, accessToken) ).then(function() {
...success function...
});
...but the $.when function triggers with the arrayname values still undefined. From the function's standpoint the deferred object is resolved even though the object values have not yet been set. I've since tried a number of ways to make the API object become deferred based on the completing of the entire ajax call and the setting of the variables, but I'm a bit stuck on the best way to do this.
Any pointers would be most appreciated! Thanks.
You could pass the callback function when you create the object, like so:
function API(uid,accessToken, callback){
$.ajax("path/to/file", {
type: "POST",
data: { user: uid, auth: accessToken },
dataType: "json",
success: function(jsonData) {
arrayname = jsonData[values]
callback(jsonData[values])
}
});
}
and then instantiate the object like so
var api = new API(uid, accessToken, function(array) {
// success function
});
If the problem is due to the "success" callback running after the "then" callbacks, you could try turning success callback into a then callback as well. I don't use JQuery but I guess it would look something like:
function API(uid,accessToken){
return $.ajax("path/to/file", {
type: "POST",
data: { user: uid, auth: accessToken },
dataType: "json",
}).then(function(jsondata){
arrayname = jsondata[values]
});
}
$.when( API = new API(uid, accessToken) ).then(function() {
// ...
});
The reason you use $.when is when you are correlating the callbacks of multiple promises, async tasks, etc. Since jQuery 1.5, all calls to $.ajax and all the wrappers ($.get and $.post) all return promises. Therefore you don't need to wrap this call with the $.when statement unless you want to do $.when(ajaxCall1, ajaxCall2).
Since you want to filter the result from the server, you should use the pipe method of promises:
function API(uid, accessToken)
return $.post(
type: 'POST'
,data: { user: uid, auth: accessToken }
,dataType: 'json'
)
.pipe(function(json) {
return json[values];
})
;
}
This allows you to write your code the way you desire:
API(uid, token)
.then(
// success state (same as promise.done)
function(arrayname /* named from your sample script*/) {
alert('success! ' + arrayname);
}
// error state (same as promise.fail)
,function(jqXHR, status, error) {
console.warn('oh noes!', error);
}
)
.done(function() { /* done #2 */ })
.fail(function() { /* fail #2 */ })
;
Note: promise.pipe() also allows you to filter (change the data passed to) the error callback as well.