Backbone + Promises - How to fetch after post - javascript

I have a model which has two functions - one posts, and one gets.
The fetch:
fetch: function (options) {
var self = this;
return P1Comm.get({
'dataType': 'json',
'processAjaxResponse': self.processAjaxResponse,
'onStatusInvalid': function (e) {
P1.log(e, 'status');
},
'onSuccess': options.success,
'onError': function (e) {
P1.log(e);
},
'sourceURL': P1.API_APPS_ROOT + 'v1.0/accepted-terms'
});
},
The post:
acceptTerms: function (appId) {
var self = this;
self.set({
'app_id': parseInt(appId,10)
});
self.createEdit(self.toJSON(), {
}).pipe(function () {
var def = $.Deferred();
var saveOpts = {
dataType: 'json',
stringify: true,
contentType: 'application/json',
processData: false,
processAjaxResponse: self.processAjaxResponse
};
self.save(self.toJSON(), saveOpts);
return def.promise();
});
},
Calling the post from the view: acceptAppTerms.acceptTerms(1);
These both work in their own right but I want to change this so that:
- Make the POST acceptTerms()
- Then make the GET fetch()
- Then call another function
I think it would be something like this (at least for the first two points):
acceptAppTerms.acceptTerms(id).then(function () {
this.launchApp(event, id);
});
But I get the error of Uncaught TypeError: Cannot read property 'then' of undefined
I am not that good at front-end - can someone point me in the right direction?
Thanks
UPDATE:
fetchAcceptedTerms: function () {
var self = this;
this.appAcceptedTerms = new T1AppAcceptedTerms();
this.acceptedTerms = new AppAcceptedTerms();
this.acceptedTerms.fetch({
success: function (data) {
if (data.meta.status === 'success') {
self.appAcceptedTerms.set(data.data);
}
}
});
},
Example below works but it breaks the setting of the data into the model.

The reason you get the error is because your acceptTerms doesn't return anything, or in other words returns undefined which doesn't have a then() method.
Your code should be something like:
fetch: function(options) {
var self = this;
return P1Comm.get({
dataType: 'json',
processAjaxResponse: self.processAjaxResponse,
onStatusInvalid: function(e) {
P1.log(e, 'status');
},
onSuccess: options.success,
onError: function(e) {
P1.log(e);
},
sourceURL: P1.API_APPS_ROOT + 'v1.0/accepted-terms'
});
},
acceptTerms: function(appId) {
var self = this;
self.set({
'app_id': parseInt(appId, 10)
});
return self.createEdit(self.toJSON(), {}).pipe(function() {
//---------------------------------------^ better use .then()
var saveOpts = {
dataType: 'json',
stringify: true,
contentType: 'application/json',
processData: false,
processAjaxResponse: self.processAjaxResponse
};
return self.save(self.toJSON(), saveOpts);
});
},
This code asummes that P1Comm.get, self.createEdit and self.save returns a promise object. If they don't then you need to create a Deferred object and return it's promise from these functions. And inside the success/error callbacks of their asynchronous actions you need to resolve/reject the promise accordingly.
I'd also like to mention that .pipe() is a deprecated method, you should use then() unless you're using ancient version of jQuery.

Related

TypeError: this.abc is not a function | javascript

Not a fronted or JavaScript so not able to understand why it's not able to find the defined function in the file. I am integrating Apple Pay and I am trying to call the back-end API based on certain event. Here is my code.
ACC.payDirect = {
_autoload: ['payDirect'],
session: null,
payDirect: function () {
let button = $('#mbutton');
if (button.length === 0) {
return;
}
$('#mbutton').on('click', this.onButtonClicked.bind());
},
onButtonClicked: function () {
if (!Session) {
return;
}
var request = getCartPaymentRequest();
this.requestSession("1234"); //getting error while calling this function
session.begin();
},
requestSession: function (validationURL) {
return new Promise(function (resolve, reject) {
$.ajax({
type: 'POST',
url: ACC.config.encodedContextPath + '/checkout/request_session',
data: JSON.stringify({ validationURL: validationURL }),
dataType: 'json',
contentType: 'application/json',
success: resolve,
error: reject
});
});
},
},
function getCartPaymentRequest() {
var result = "";
$.ajax({
type: 'GET',
url: ACC.config.encodedContextPath + '/checkout/payment-request',
dataType: 'json',
async: false,
success: function (response) {
result = response;
},
});
return result;
}
While calling the requestSession I am getting the following error
TypeError: this.requestSession is not a function. (In 'this.requestSession("1234")', 'this.abc' is undefined)
I am sure doing some basic mistake but not able to find out the root cause as why it's not able to find the second function while the first one seems to be working without any issue.
The problem is with the .bind method you called without any parameters.
Take the following example:
const obj2 = {
b: function (callback) {
callback()
}
};
const obj = {
a: function () {
obj2.b(this.c.bind())
},
c: function () {
console.log(this)
}
};
obj.a()
What will happen here is that the Window object will appear in the console.
This happens because the window is somewhat of the global context, and when running in the browser JavaScript's bind's parameter will default to the only context it can: the window.
What you need to do is call the .bind method using the ACC.payDirect as parameter as it will then bind it to the object and use the proper context, you could use a this but if the methodm was called with a different context would cause problems. An even better solution (provided you can use new ES features) is using arrow functions. They are much, much better to work with and won't give you headaches such as this.

Many requests with final callback, unreliable order?

I'm trying to come up with a resource loader if you will, that will load many remote resources and then execute a final callback (like rendering a DOM based on the retrieve data from these requests).
Here's the function:
var ResourceLoader = function () {
this.requests = new Array();
this.FinalCallback;
this.Add = function (request) {
this.requests.push(request);
};
this.Execute = function() {
for (var x = 0; x < this.requests.length ; x++) {
var success = this.requests[x].success;
//if this is the last of the requests...
if (x == (this.requests.length - 1) && this.FinalCallback) {
$.when($.ajax({
url: this.requests[x].url,
dataType: 'json',
error: this.requests[x].error,
method: 'GET'
}).done(success)).then(this.FinalCallback);
}
else {
$.ajax({
url: this.requests[x].url,
dataType: 'json',
error: this.requests[x].error,
method: 'GET'
}).done(success);
}
}
};
};
And here's how I use it:
var apiUrl = Utilities.Api.GetWebApiUrl();
var loader = new Utilities.ResourceLoader();
loader.Add({
url: apiUrl + 'regions/get',
success: function (results) {
Filters.Regions = results;
}
});
loader.Add({
url: apiUrl + 'currentfactors/get/83167',
success: function (results) {
Filters.NbrEmployees = results;
}
});
loader.Add({
url: apiUrl + 'currentfactors/get/83095',
success: function (results) {
Filters.Industries = results;
}
});
loader.FinalCallback = RenderBody;
loader.Execute();
function RenderBody() {
console.log('render...');
}
Obviously, I'm expecting RenderBody to be executed last. But that's not what happening. What's ironic is that I remember doing something like that before, but lost the code... Looks like I'm having a brainfart here.
As you've tagged with promise - here's a really clean solution that uses Promise.all
this.Execute = function() {
Promise.all(this.requests.map(function(request) {
return $.ajax({
url: request.url,
dataType: 'json',
error: request.error,
method: 'GET'
}).done(request.success);
})).then(this.FinalCallback);
};
or ... using JQuery when
this.Execute = function() {
$.when.apply($, this.requests.map(function(request) {
return $.ajax({
url: request.url,
dataType: 'json',
error: request.error,
method: 'GET'
}).done(request.success);
})).then(this.FinalCallback);
};
Es6 Promise has solutions for your problem, there is no need to reinvent it unless the loading of resource groups is a specific goal to abstract. Set up a Promise object for each resource request, using the constructor to assign the resolve and reject callbacks appropriately for the XHR. Keep a collection (any Iterable will do) of individualPromise.then(individualCallback) results. Your final product is obtained by Promise.all(collectionOfPromises).then(finalCallback).

How to replace 'Async=false' with promise in javascript?

I have read a lot about promises but I'm still not sure how to implement it.
I wrote the folowing AJAX call with async=false in order for it to work, but I want to replace it with promise as I saw that async=false is deprecated.
self.getBalance = function (order) {
var balance;
$.ajax({
url: "/API/balance/" + order,
type: "GET",
async: false,
success: function (data) {
balance = data;
},
done: function (date) {
}
});
return balance;
}
Would you be able to help me? I just need an example to understand it.
As first point, you don't want to set an asynchronous call to false as it will lock the UI.
You could simplify your method returning the ajax object and the handle it as a promise.
self.getBalance = function (orderNumber) {
return $.ajax({
url: "/Exchange.API/accountInfo/balance/" + orderNumber,
type: "GET",
});
};
var demoNumber = 12;
self.getBalance(demoNumber).then(function(data){
console.log(data);
},function(err){
console.log("An error ocurred");
console.log(err);
});
Return promise object from getBalance method:
self.getBalance = function (orderNumber) {
return $.ajax({
url: "/Exchange.API/accountInfo/balance/" + orderNumber,
type: "GET"
});
}
and use it later like this:
service.getBalance().then(function(balance) {
// use balance here
});

Why is then() chained methods not running sequentially?

We are trying to execute a number of AJAX calls in a particular order. The following code has methodA, methodB and methodC (each returns an AJAX promise object running async=true).
They are chained using the then() function in jQuery.
self.methodA().then( self.methodB() ).then( self.methodC() )
I have made one of the methods slow (methodB) (I use a slow URL).
I would expect A... 10 second wait...then B then C.
Instead I get A, C ....10 second wait and B.
Why is it doing that? Does it have something to do with me using the alert() in an always() function?
Here is my code in a fiddle :
http://jsfiddle.net/h8tfrvy4/13/
Code:
function Tester() {
var self = this;
this.url = 'https://public.opencpu.org/ocpu/library/';
this.slowurl = 'http://fake-response.appspot.com/?sleep=5';
this.save = function() {
self.methodA().then( self.methodB() ).then( self.methodC() )
}
this.methodA = function () {
var self = this;
return $.ajax({
url: self.url,
async: true
}).always(function (processedDataOrXHRWrapper, textStatus, xhrWrapperOrErrorThrown) {
//check for errors... and if OK
alert('A OK');
})
}
this.methodB = function () {
var self = this;
return $.ajax({
url: self.slowurl,
async: true
}).always(function (processedDataOrXHRWrapper, textStatus, xhrWrapperOrErrorThrown) {
//check for errors... and if OK
alert('B OK');
})
}
this.methodC = function () {
var self = this;
return $.ajax({
url: self.url,
async: true
}).always(function (processedDataOrXHRWrapper, textStatus, xhrWrapperOrErrorThrown) {
//OK
alert('C OK');
})
}
}
new Tester().save();
This is wrong:
self.methodA().then( self.methodB() ).then( self.methodC() )
You're invoking each method immediately, and passing the promises into then.
If you want each function to wait until the previous finishes, you need to give each then a callback to execute when the previous promise resolves:
self.methodA().then(function () { return self.methodB() }).then(function() { return self.methodC() });
Short and simple:
this.save = function() {
self.methodA().then( self.methodB ).then( self.methodC )
}
It has bothered me all **&^*$& day that #meagar was right and I was wrong on this one but I was sure that I was right. His answer seemed too complicated and but I was fuzzy headed in the morning and my answer wasn't right either. This is the right answer and it works perfectly when you plug it into the JSFiddle.

Issue in AJAX and JavaScript Object

I am trying to write a JavaScript Object which has many Properties and Methods. The basic function of this code is to send an ajax call and get data from server.
Code IS:
function restClient(options) {
var _response;
var _response_status;
var _response_message;
var _response_data;
// Default Options
var _default = {
restCall: true,
type: "GET",
url: '',
contentType: "application/json; charset=utf-8",
crossDomain: false,
cache: false,
dataType: 'json',
data: {},
beforeSend: _ajaxBeforeSend,
success: _ajaxSuccess,
error: _ajaxError,
complete: _ajaxComplete
};
// Extend default Options by User Options
var ajaxOptions = $.extend(_default, options);
// Private Methods
function _ajaxBeforeSend() {
}
function _ajaxSuccess(response) {
_response = response;
_response_status = response.status;
_response_message = response.message;
_response_data = response.data;
}
function _ajaxError(xhr, status, error) {
_response_status = xhr.status;
_response_message = error;
}
function _ajaxComplete(xhr, status) {
}
// Send Ajax Request
this.sendRequest = function() {
$.ajax(ajaxOptions);
};
// Get Server Response Pack [status,message,data]
this.getResponse = function() {
return _response;
};
// Get Server Response Status: 200, 400, 401 etc
this.getStatus = function() {
return _response_status;
};
// Get Server Message
this.getMessage = function() {
return _response_message;
};
// Get Server Return Data
this.getData = function() {
return _response_data;
};
}
Now I am trying to create object using new operator and call sendRequest(); method to send an ajax call and then I am calling getResponse(); to get server response like:
var REST = new restClient(options);
REST.sendRequest();
console.log(REST.getResponse());
Every thing is working properly But the problem is REST.getResponse(); call before to complete Ajax which give me empty result. If i do like this
$(document).ajaxComplete(function(){
console.log(REST.getResponse());
});
then it work But Still two problems are
If there are another ajax call its also wait for that
its looking bad I want to hide this ajaxComplete() some where within restClient();
Please Help me.
Thanks.
You have to change method sendRequest to accept a callback, that you'll call on response completion.
this.sendRequest = function(cb) {
this.cb = cb;
$.ajax(ajaxOptions);
};
this._ajaxComplete = function(xhr, status) {
this.cb && this.cb();
}
Also, after defining this._ajaxComplete change the _default.complete handler, in order to bind the this object, otherwise you'll miss the cb property:
_default.complete = this._ajaxComplete.bind(this);
Your client code will become:
var REST = new restClient(options);
REST.sendRequest(function(){
console.log(REST.getResponse());
});

Categories

Resources