How to replace 'Async=false' with promise in javascript? - 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
});

Related

How to pass the result of an Asynchronous function as a deffered object

i'm trying to get my head around working with Deffered objects especially in cases where you have to perform multiple asynchronous operations on every item in an array. In the code below i just want to be able to access the result of an asynchronous array after it is complete, in my case it is the results array.
To explain the code below
ListData function derives the source data which is an array that i intend to manipulate.
getPictureComplete1 perfroms an async operation (ListDataWithPicture) on every item in the array above
The idea of step 2 was to add an image url to every item in the array from Step 1 and then use the new array as an input to step 4
could be to print the images to the page or perform additional manipulations on the array
var mydeferred = $.Deferred();
var ListData = function (){
listName = 'TeamInfo';
$.ajax({
url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('"+listName+"')/items?$select=Name/Title,Name/Name,Name/Id,Name/EMail,Name/WorkPhone&$expand=Name/Id",
type: "GET",
headers: { "ACCEPT": "application/json;odata=verbose" },
success: onQuerySucceded,
error: onQueryFailed
});
return mydeferred.promise();
}
var ListDataWithPicture = function(userId, callback) {
// execute AJAX request
$.ajax({
url: _spPageContextInfo.siteAbsoluteUrl + "/_api/web/SiteUserInfoList/items?$filter=Id eq " + userId + "&$select=Picture",
type: "GET",
async: false,
headers: { "ACCEPT": "application/json;odata=verbose" },
success: function(data){
console.log("Starting async operation for " + userId);
var pictureLink = "";
var mydata = callback(data.d.results[0].Picture.Url);
return mydata
},
error: onQueryFailed
});
}
function onQuerySucceded (data){
var PeopleCompleteList = [];
for (i=0; i< data.d.results.length; i++) {
//check if the user exists if he does store the following properties name,title,workphone,email
if(data.d.results[i]['Name'] != null){
personName = data.d.results[i]['Name'].Name.split('|')[2];
userName = data.d.results[i]['Name']['Name'];
UserTitle = data.d.results[i]['Name']['Title'];
UserphoneNumber = data.d.results[i]['Name']['WorkPhone'];
UserEmail = data.d.results[i]['Name']['EMail'];
Id = data.d.results[i]['Name']['Id'];
PeopleCompleteList.push(PersonConstructor(personName, UserTitle, UserphoneNumber,UserEmail,Id));
}
}
mydeferred.resolve(PeopleCompleteList);
}
function getPictureComplete1 (data){
var def = new $.Deferred();
var results = [];
var expecting = data.length;
data.forEach(function(entry, index) {
//this is the asynchronous function
ListDataWithPicture(entry.UserId, function(result) {
results[index] = {imageUrl: result, UserId: entry.UserId, name: entry.name, Title: entry.Title, phoneNumber: entry.phoneNumber, Email: entry.Email};
//console.log(result);
if (--expecting === 0) {
// Done!
console.log("Results:", results); //this works succeffully from here
def.resolve();
return results
//mydeferred.resolve(results);
}
});
});
return mydeferred.promise();
}
$(function () {
ListData().then(function(data){
//how can i access the results array in this function after it has completed??
var value = getPictureComplete1 (data);
//the line below results undefined,which i understand because the getPictureComplete1 function may not have completed at the time
console.log(value);
});
Because this is an asynchronous operation, the only time that you have access to the array of results from your ajax call in LineData is within the scope of the onQuerySucceded function that you assigned to the success property in your ajax configuration. The function you define here will be fired after a successful response was received.
When establishing a deferred promise, you will need to define what data resolves the promise. Whatever you pass into the resolve method will be available as a parameter within a subsequent then block on your promise chain.
I'm not seeing onQuerySucceded defined in your example, but it would look something like this:
var ListData = function (){
var mydeferred = $.Deferred();
listName = 'TeamInfo';
$.ajax({
url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('"+listName+"')/items?$select=Name/Title,Name/Name,Name/Id,Name/EMail,Name/WorkPhone&$expand=Name/Id",
type: "GET",
headers: { "ACCEPT": "application/json;odata=verbose" },
success: function onQuerySuccess(data) {
mydeferred.resolve(data);
},
error: onQueryFailed
});
return mydeferred.promise();
}
Now, after a successful call, the promise will resolve with the data. So, something like this is possible:
ListData()
.then(function (data) {
// do something with the data
})
Similarly, you will want to define what are the rejection cases for your deferred promise. For example, maybe the ajax call doesn't succeed. For this, you will want to use the reject method on the deferred object.
For example:
var ListData = function (){
var mydeferred = $.Deferred();
listName = 'TeamInfo';
$.ajax({
url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('"+listName+"')/items?$select=Name/Title,Name/Name,Name/Id,Name/EMail,Name/WorkPhone&$expand=Name/Id",
type: "GET",
headers: { "ACCEPT": "application/json;odata=verbose" },
success: function onQuerySuccess(data) {
mydeferred.resolve(data);
},
error: function onQueryFailed(error) {
mydeferred.reject(error);
}
});
return mydeferred.promise();
}
This works similarly to resolve, but will resolve to any subsequent catch or fail blocks. So, something like this will work
LineData()
.then(function (data) {
// it was successful, do something with the data
})
.catch(function (error) {
// There was an error. The then block was not called.
// Do something with the error.
})
Put even more simply, you can set mydeferred.resolve and mydeferred.reject as these properties directly. Like so:
var ListData = function (){
var mydeferred = $.Deferred();
listName = 'TeamInfo';
$.ajax({
url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('"+listName+"')/items?$select=Name/Title,Name/Name,Name/Id,Name/EMail,Name/WorkPhone&$expand=Name/Id",
type: "GET",
headers: { "ACCEPT": "application/json;odata=verbose" },
success: mydeferred.resolve,
error: mydeferred.reject
});
return mydeferred.promise();
}
You will want to do something similar with your ListDataWithPicture function so that it also returns a promise with the data that you need:
var ListDataWithPicture = function(userId, callback) {
var deferred = $.Deferred();
$.ajax({
url: _spPageContextInfo.siteAbsoluteUrl + "/_api/web/SiteUserInfoList/items?$filter=Id eq " + userId + "&$select=Picture",
type: "GET",
async: false,
headers: { "ACCEPT": "application/json;odata=verbose" },
success: function(data){
deferred.resolve(data.d.results[0].Picture.Url)
},
error: deferred.reject
});
return deferred.promise();
}
This allows you to do something like this:
LineData()
.then(function (data) {
// performing on just the first result:
return ListDataWithPicture(data[0]);
})
.then(function (url) {
// do something with the result
})
.catch(function (error) {
// do something with the error
});
Because you want to perform a aynchronous operation on each of the items in your array, I'd recommend to use Promise.all which executes and resolves an array of promises.
LineData()
.then(function (data) {
// create a map of promises
var promises = data.map(function (item) {
return ListDataWithPicture(item);
});
return Promise.all(promises);
})
.then(function (urls) {
// urls will be an array of urls resolved from calling
// ListDataWithPicture on each item in the array resolved
// from above
})
.catch(function (error) {
// do something with the error
});
Here are some good resources to learn more:
http://api.jquery.com/category/deferred-object/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

Sequentially execute two functions with jQuery in for loops

I'm pretty new to Javascript. Please don't make it too harsh :)
I have two functions, both of which involve executing jQuery requests within for loops. For example,
function a(n,locations) {
for (var i = 0; i < n; i ++) {
$.ajax({
url: 'https://geocoder.cit.api.here.com/6.2/geocode.json',
type: 'GET',
dataType: 'jsonp',
jsonp: 'jsoncallback',
data: {
searchtext: input,
app_id: APP_ID,
app_code: APP_CODE,
},
success: function (data) {
handleData(data,locations);
}
});
}
The handleData() function would make changes to the empty array locations from the jQuery data. My function b(m) is of similar format but would use the updated locations as input.
Now, I have a c(n,m) in which I would like execute a() and b() sequentially:
function c(n,m) {
var locations = [];
a(n,locations);
b(m,locations);
}
From previous answers I understand that sequentially executing functions involving jQuery calls can be achieved by using promises (such as .then). However, this solution is only applicable when a(n) returns a promise, which is not achievable under the for-loop structure. Could you please share your insights on how to solve this issue? Thanks in advance for the help.
I would suggest recursion instead of your for loop. For example, you can call the function recursionExample like this,
function a(n) {
return new Promise ((resolve, reject) {
(function recursionExample(a) {
if (a === n) {
resolve;
} else {
$.ajax({ url: 'https://geocoder.cit.api.here.com/6.2/geocode.json',
type: 'GET',
dataType: 'jsonp',
jsonp: 'jsoncallback',
data: {
searchtext: input,
app_id: APP_ID,
app_code: APP_CODE,
},
success: function(data) {
handleData(data);
recursionExample(a + 1);
}
});
}
})(0);
});
}
This will then allow you to use the promise and .then functions. Like so...
function c(n,m) {
var locations = [];
a(n,locations)
.then (function() {
b(m,locations);
});
}

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 do I wait for callback? [duplicate]

This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 7 years ago.
I call a function which registers a registrationId is issued using chrome.gcm. Everything is fine but because the callback takes time, my code does not work without a console.log or alert. Any tips how I can make it wait?
var registrationId = ""
function register() {
var senderId = 'MY_SENDER_ID';
chrome.gcm.register([senderId], registerCallback);
}
function registerCallback(regId) {
registrationId = regId;
if (chrome.runtime.lastError) {
return false;
}
chrome.storage.local.set({registered: true});
}
$(function(){
$("#register-form").submit(function(e) {
//Disable from further calls
$('#submit').disabled = true;
register()
var name = $('#name').val()
var email = $('#email').val()
//Insert console.log or alert here to slow it down
var chromeId = registrationId
$.ajax({
type: "POST",
url: 'MY_URL',
ajax:false,
data: {chromeId: chromeId, name: name, email:email},
success: function(result)
{
console.log(result)
}
});
});
})
You need to execute the method as part of the callback, since the value that needs to be passed in as part of your AJAX request, is available only after ASYNC process completes.
You can use a Deferred objects in such cases. As soon as the it is resolved you can execute your AJAX call.
$(function() {
$("#register-form").submit(function(e) {
//Disable from further calls
$('#submit').disabled = true;
var senderId = 'MY_SENDER_ID';
// Store the promise in a variable
var complete = chrome.gcm.register([senderId]);
// When resolved it will, hit the callback
// where you have access to the value
// which is then passed to your AJAX request
$.when(complete).done(function(regId) {
var registrationId = regId;
if (chrome.runtime.lastError) {
return false;
}
chrome.storage.local.set({
registered: true
});
$.ajax({
type: "POST",
url: 'MY_URL',
ajax: false,
data: {
chromeId: registrationId,
name: name,
email: email
},
success: function(result) {
console.log(result)
}
});
});
});
});
The code that comes after register() should go in a new callback which accepts registrationId as a parameter, and is passed to register(). Then, register() can call this callback with the registrationId it gets back from chrome.gcm.register. No need for the global registrationId variable.
function register(callback) {
var senderId = 'MY_SENDER_ID';
chrome.gcm.register([senderId], function (regId) {
if (chrome.runtime.lastError) {
return false;
}
chrome.storage.local.set({registered: true});
callback(regId);
});
}
$(function(){
$("#register-form").submit(function(e) {
//Disable from further calls
$('#submit').disabled = true;
register(function (registrationId) {
var name = $('#name').val()
var email = $('#email').val()
//Insert console.log or alert here to slow it down
var chromeId = registrationId
$.ajax({
type: "POST",
url: 'MY_URL',
ajax:false,
data: {chromeId: chromeId, name: name, email:email},
success: function(result)
{
console.log(result)
}
});
});
});
})
Promises and async/await helps with stuff like this, also.

How to wait ajax callback result from another callback?

I have a method below:
self.getOrAddCache = function (key, objectFactory) {
var data = self.getFromCache(key);
if (!data) {
data = objectFactory();
if (data && data != null)
self.addToCache(key, data);
}
return data;
};
I use like this:
function getCities()
{
var cities = getOrAddCache(CacheKeys.Cities, function() {
var cityArray = new Array();
// get city informations from service
$.ajax({
type: "GET",
async: true,
url: "service/cities",
success: function (response) {
$.each(response, function(index, value) {
cityArray.push({
name: value.name,
id: value.id
});
});
}
});
if (cityArray.length > 0)
return cityArray;
else {
return null;
}
});
return cities;
}
getCities function always return null because getCities not waiting for completion async ajax request.
How can i resolve this problem? (Request must be async)
The best solution for this is to use Deferred objects. Since you require your AJAX call to be asynchronous, you should have your getCities function return a promise to return that data at some point in the future.
Instead of storing the raw data in the cache, you store those promises.
If you request a promise that has already been resolved, that will complete immediately. If there's already a pending request for the cached object, the async AJAX call will be started and all outstanding callbacks waiting for that promise will be started in sequence.
Something like this should work, although this is of course untested, E&OE, etc, etc.
self.getCached = function(key, objectFactory) {
var def = self.getCache(key);
if (!def) {
def = objectFactory.call(self);
self.addToCache(key, def);
}
return def;
}
function getCities() {
return getCached(CacheKeys.Cities, function() {
return $.ajax({
type: 'GET',
url: 'service/cities'
}).pipe(function(response) {
return $.map(response, function(value) {
return { name: value.name, id: value.id };
});
});
});
}
Note the usage of .pipe to post-process the AJAX response into the required format, with the result being another deferred object, where it's actually the latter one that gets stored in your cache.
The usage would now be:
getCities().done(function(cities) {
// use the cities array
});
With a callback:
function getCities(callbackFunction)
{
var cities = getOrAddCache(CacheKeys.Cities, function() {
var cityArray = new Array();
// get city informations from service
$.ajax({
type: "GET",
async: true,
url: "service/cities",
success: function (response) {
$.each(response, function(index, value) {
cityArray.push({
name: value.name,
id: value.id
});
});
callbackFunction(cityArray);
}
});
});
}
getCities(function(cityArray){
// do stuff
});
You can't return the result from a function fetching asynchronously the data.
Change your getCities function to one accepting a callback :
function fetchCities(callback) {
var cities = getOrAddCache(CacheKeys.Cities, function() {
var cityArray = new Array();
// get city informations from service
$.ajax({
type: "GET",
async: true,
url: "service/cities",
success: function (response) {
$.each(response, function(index, value) {
cityArray.push({
name: value.name,
id: value.id
});
});
if (callback) callback(cityArray);
}
});
});
}
And use it like this :
fetchCities(function(cities) {
// use the cities array
});
Note that it's technically possible, using async:true, to make the code wait for the response but don't use it : that's terrible practice and it locks the page until the server answers.
You seem to be contradicting yourself.
Something that is asynchronous, by definition, does not pause the script to wait for the end of it's execution. If it does wait, it cannot be asynchronous.
The best wayto fix this is by adding a callback function in your ajax success function that passes the end result to another function, which handles the rest of the execution.

Categories

Resources