My 1st post. I'm a Javascript 'hacker' (and feel much more comfortable with PHP, never gotten a hang of Javscript):
I've tried for hours trying callbacks, delays, now ASYNC/AWAIT:
Problem: AWAIT Code Not doing what I want.
I've referenced multiple examples and forums, but not turn to stack overflow in desperate frustration!
Note: Expanding on this existing tool: https://github.com/webismymind/editablegrid/tree/master/examples
(notice it says be sure to run AJAX in Synchronous)
DatabaseGrid.prototype.initializeGrid = function(grid) {
var self = this;
grid.setEnumProvider('id_Bolt', new EnumProvider({
// the function getOptionValuesForEdit is called each time the cell is edited
// here we do only client-side processing, but you could use Ajax here to talk with your server
// if you do, then don't forget to use Ajax in synchronous mode
getOptionValuesForEdit: **async** function (grid, column, rowIndex) {
var Thread_Code = grid.getValueAt(rowIndex, grid.getColumnIndex("Thread_Code"));
**let** result = **new** setBoltEnum(Thread_Code);
*console.log(Date.now() + " - 3 - " + **await** result.array);*
return **await** result.array;
*console.log(Date.now() + " - 4 - " + "ending function");*
}
}));
};
AJAX code it is calling:
setBoltEnum = function(Thread_Code){
*console.log(Date.now() + " - 1 - calling AJAX");*
result = $.ajax({
url: 'EnumHelper.php',
type: 'POST',
dataType: "html",
data: {
value: Thread_Code,
col_out: 'Descr',
col_search: 'Thread_Code',
tablename: 'Bolt_List'
},
success: function (response) {
result = JSON.parse(response);
*console.log(Date.now() + " - 2 - got AJAX response: " + result.resp);*
return result;
//_callback();
},
error: function(XMLHttpRequest, textStatus, exception) { alert("Ajax failure\n" + errortext); },
async: true
});
};
Output in Chrome:
1 - calling AJAX
3 - undefined
2 - Got AJAX response - ok
(4) is not outputted
How can I get code to run 1-2-3-4?
You can only usefully await a promise.
You call setBoltEnum with new so it is treated as a constructor function and returns an object that is an instance of setBoltEnum (this doesn't make any sense because that function doesn't do anything that would be useful to use a constructor function for). It certainly doesn't return a promise.
setBoltEnum then calls $.ajax (which runs asynchronously) and assigns the result to result (You don't seem to have declared result so it is a global… this is probably bad, especially when dealing with asynchronous code). It then does nothing with that variable. It has no return statement at all so even if you didn't call it with new it will wouldn't return a promise.
Then you return await result.array;. There's no point in awaiting as you return since you are going to be returning a promise anyway (because the function is async). Here, result is the return value of $.ajax() which won't have an array property so the value is undefined (not a promise).
The next line is console.log but you never reach it because you just returned.
Later the success function is called. This assigns a new value to result which might have an array property… but that also won't be a promise.
To fix this:
Don't use new when you aren't dealing with a constructor function
Don't use await when you aren't dealing with a promise or other thenable
Do return promises or other thenables from functions that work asynchronously
Don't mix callbacks (success / error) with promises (it just makes code hard to manage)
Do remember that $.ajax() returns a thenable (which you can await).
Below code ended up working and outputted 1-2-3. Note, I stumbled upon this (adding ASYNC/AWAIT in setBoltEnum).
setBoltEnum = async function(Thread_Code){
console.log(Date.now() + " - 1 - calling AJAX");
return await $.ajax({
url: 'EnumHelper.php',
type: 'POST',
dataType: "html",
data: {
value: Thread_Code,
col_out: 'Descr',
col_search: 'Thread_Code',
tablename: 'Bolt_List'
},
success: function (response) {
result = JSON.parse(response);
console.log(Date.now() + " - 2 - got AJAX response: " + result);
},
error: function(XMLHttpRequest, textStatus, exception) { alert("Ajax failure\n" + errortext); },
async: true
});
};
DatabaseGrid.prototype.initializeGrid = function(grid) {
var self = this;
grid.setEnumProvider('id_Bolt', new EnumProvider({
// the function getOptionValuesForEdit is called each time the cell is edited
// here we do only client-side processing, but you could use Ajax here to talk with your server
// if you do, then don't forget to use Ajax in synchronous mode
getOptionValuesForEdit: async function (grid, column, rowIndex) {
var Thread_Code = grid.getValueAt(rowIndex, grid.getColumnIndex("Thread_Code"));
result = await setBoltEnum(Thread_Code);
console.log(Date.now() + " - 3 - returning this: " + result);
return result;
//return expecting this { "br" : "Brazil", "ca": "Canada", "us" : "USA" }; //result;
}
}));
Related
So, I have a function which does some asynchronous work.
The function currently looks like this (setTimeout currently is implemented because I haven't managed to implement the async/await way correctly):
function gatherAllRelevantReservationData(TimeFrameStartFromInputfield, TimeFrameEndFromInputfield, liebraum, liebsitz, reservationFromDatabaseTimeFramesStart, reservationFromDatabaseTimeFramesEnd){
console.log("reservationFromDatabaseTimeFramesStart inside gatherAllRelevantReservationData are ", reservationFromDatabaseTimeFramesStart, reservationFromDatabaseTimeFramesEnd)
//console.log("TimeFrameStartFromInputfield and TimeFrameEndFromInputfield inside gatherAllRelevantReservationData are ", TimeFrameStartFromInputfield, TimeFrameEndFromInputfield)
var timeFrameAsObject = convertDateStringToJavaScriptDateTimeObject(TimeFrameStartFromInputfield, TimeFrameEndFromInputfield)
var startDateAsObject = timeFrameAsObject.start
var endDateAsObject = timeFrameAsObject.end
var timeFrameAsUnixTimeStamp = ConvertToCustomizedUnixTimestampString(startDateAsObject, endDateAsObject)
var startDateAsUnixTimeStamp = timeFrameAsUnixTimeStamp.start;
var endDateAsUnixTimeStamp = timeFrameAsUnixTimeStamp.end;
getTable(startDateAsUnixTimeStamp, endDateAsUnixTimeStamp, liebraum);
if(FavSeatcheckHasBeenEnabled == 1){
setTimeout(function(){
prepareSelectedAndDatabaseDateStringsForComparison(startDateAsObject, endDateAsObject, liebraum, liebsitz, reservationFromDatabaseTimeFramesStart, reservationFromDatabaseTimeFramesEnd);
}, 300)
}
};
The getTable function requires some time and needs to have finished before prepareSelectedAndDatabaseDateStringsForComparison is called. The problem is that getTable doesn't have any return value.
I'm pretty new to ES7 async/await features as well as to ES6 promises.
I know that await usually expects some promise to be returned, and I probably could arrange this in some very hacky, nasty way.
But I'd like to know if there is any other, elegant way around this.
Ideally, I'd just like to attach async to gatherAllRelevantReservationData and then put an "await" in front of getTable call, but this of course did not work.
Any ideas how I could solve this?
EDIT: Here is the "getTable" function:
function getTable(start, ende, liebraum)
{
//console.log("start in getTable is " + start)
//console.log("ende in getTable is " + ende)
fillRooms(liebraum);
$.post("../include/returnTable.php", {
anfang: start,
ende: ende,
art: art
}, function(data){
document.getElementById("tablediv").innerHTML= data;
console.log("start inside callback of AJAX inside getTabel is ", start)
//console.log("data after getTable function " + data);
//fillRooms(liebraum);
})
}
If you wish to use async/await, you need to change getTable to return a Promise (or some thenable), because only they can be awaited.
Fix your getTable to return the $.post call, so that its success can then be detected in gatherAllRelevantReservationData function, and then you can simply await the call of getTable:
async function gatherAllRelevantReservationData(...) {
...
await getTable(...);
...
}
function getTable(start, ende, liebraum) {
fillRooms(liebraum);
return $.post("../include/returnTable.php", {
anfang: start,
ende: ende,
art: art
}, function(data) {
document.getElementById("tablediv").innerHTML = data;
})
}
I want to control the number of ajax calls to a controller using a while loop.
var counter = 0;
$('#filter-form').submit(function (event) {
event.preventDefault();
alert("counter init = " + counter)
while (counter < 10) {
(function () {
$.ajax({
url: '/algorithm',
method: 'GET',
data: $('#filter-form').serialize() + "&counter=" + counter,
success: function (data) {
alert("The data is " + data);
setCounter(parseInt(data))
},
error: function (xhr, status, error) {
var err = eval("(" + xhr.responseText + ")");
alert(err.Message);
}
});
})();
}
alert("counter end = " + counter)
});
function setCounter(data) {
counter = data
}
Controller:
#RequestMapping(value = "/algorithm")
#ResponseBody
public String test(#RequestParam Map<String, String> allRequestParam) {
int counter = Integer.parseInt(allRequestParam.get("counter"));
counter++;
return Integer.toString(counter);
}
The controller basically just increments the counter and returns it and in the ajax success: it will set the global counter to that number.
When I do this, the page just freezes and I cannot click anything. I put the ajax call in a function for scoping but it still does not work. When I use a for loop, it seems the ajax does not invoke because I do not get any success or error alerts.
It doesn't work for a simple reason: the $.ajax call is asynchronous.
Take this example:
$(function() {
var t = 1;
console.log("Hey, the ajax will start! t's value: " + t);
$.ajax({
url: 'www.google.com.br',
method: 'GET',
success: function (data) {
t++;
console.log("We've received an answer! t's (incremented) value: " + t);
},
error: function (xhr, status, error) {
t++;
console.log("We've received an error! t's (incremented) value: " + t);
}
});
console.log("Hey, the ajax just ended.... Not really. t's value: " + t);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
The output is:
Hey, the ajax will start! t's value: 1
Hey, the ajax just ended.... Not really. t's value: 1
We've received an error! t's (incremented) value: 2
That's because the $.ajax call is nonblocking, thus is doesn't block the program until it is finished, allowing the program to keep on executing the next line of code and continue running the ajax task in the background.
It is a recurrent issue in SO, so instead of providing solutions again here I'll ask you to read more on the questions:
How do I return the response from an asynchronous call?
How can I get jQuery to perform a synchronous, rather than asynchronous, Ajax request?
What does Asynchronous means in Ajax?
while will block synchronously until its condition is reached. Even if responses come back, the response will be asynchronous; the current thread (the while loop) will keep blocking forever.
Don't block. I don't see any reason to use a loop in the first place - instead, simply test to see if the counter is greater than the allowed number, and if it is, return:
$('#filter-form').submit(function (event) {
event.preventDefault();
alert("counter init = " + counter)
if (counter >= 10) return;
If you wanted to make multiple requests in parallel on form submit, you could do that, but you would have to keep track of the counter client-side:
var counter = 0;
$('#filter-form').submit(function (event) {
event.preventDefault();
alert("counter init = " + counter)
while (counter < 10) {
counter++;
// ... make request
As others have said your problem is that the call is asynchronous. This simple example may give you some idea about how to control the flow. It should be simple enough to apply it to your case.
I am simulating what you need to make your code work. For the errors, I am passing back null but you should bubble up any errors that may occur and either halt execution or deal with them some other way.
var count = 0; // used to store your count
// This represents the function you are
// waiting on with your ajax calls
function waitOne(num, callback) {
setTimeout(() => {
callback(null, num);
}, 1000);
}
// This represents your ajax call
function callWaitOne(callback) {
waitOne(count, (err, num) => {
// Your result is here
console.log(num);
// Callback to let the control function
// know the ajax has returned
callback(null);
});
}
// This will control the calls
function printWaitOne() {
callWaitOne((err) => {
if (count < 10) {
count++;
// Only calls if its callback
// has been called.
printWaitOne();
}
});
}
printWaitOne();
I have the following function to check a users session to see if they're staff or not. Now, I know there are better ways to do this, but I'm trying to make a simple application that's tied with a forum software.
function isStaff(callback) {
$.ajax({
url: url
}).done(function(data) {
var session = $.parseJSON(data);
if (session.is_staff === 1) {
callback(true);
} else {
callback(false);
}
});
}
Let's say I'm using this function in, like so, when compiling a "post" (Handlebars).
function compilePost(post) {
var source = $('#feed-item-template').html();
var template = Handlebars.compile(source);
var context = {
id: post.id,
content: post.text,
author: post.author,
date: $.timeago(post.date),
staff: function() {
isStaff(function(response) {
return response;
});
}
}
var html= template(context);
return html;
}
Problem here, is that the request to check if a user is staff doesn't complete the request until after the function is ran.
I know with Promises is an alternative to async: false, where request is made and the response comes back before the function finishes.
But I have no idea how I can convert this into a promise. I've tried to learn it but I'm stuck at the concept. Can someone explain this to me? Thanks.
First, let's simplify the compilePost function. This function should know how to compile a post in a synchronous manner. Let's change the isStaff fetching to a simple argument.
function compilePost(post, isStaff) {
var source = $('#feed-item-template').html();
var template = Handlebars.compile(source);
var context = {
id: post.id,
content: post.text,
author: post.author,
date: $.timeago(post.date),
staff: isStaff
}
var html= template(context);
return html;
}
Now, let's create a new method, with a single purpose - checking if a user is member of the staff:
function checkForStaffMemebership() {
return new Promise(function (resolve, reject) {
$.ajax({
url: url,
success: function (data) {
var session = $.parseJSON(data);
if (session.is_staff === 1) {
resolve(true);
} else {
resolve(false);
}
}
});
});
}
This function wraps your original ajax call to the server with a promise, whenever the $.ajax call gets a response from the server, the promise will resolve with the answer whether the user is a staff member or not.
Now, we can write another function to orchestrate the process:
function compilePostAsync(post) {
return checkForStaffMemebership()
.then(function (isStaff) {
return compilePost(post, isStaff);
});
}
compilePostAsync finds out whether the user is a staff member or not. Then, it's compiling the post.
Please notice that compilePostAsync returns a promise, and thus if you used to have something like:
element.innerHTML = compilePost(post);
Now, you should change it to something like:
compilePostAsync(post).then(function (compiledPost) {
element.innerHTML = compiledPost;
});
Some notes:
This is only an example, it surely misses some things (proper error handling for example)
The isStaff and checkForStaffMemebership (original and new) do not get any argument, I guess you'd figure out how to pass the userId or any other data you might need
Read about promises, it's a useful tool to have, there is a lot of data about it on the web, for example: MDN.
As per the documentation you dont need to wrap the ajax with a promise which already implements promise. Instead chain the response as explained below.
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)
You can do something like below by chaining the response:
function isStaff(url, post) {
return $.ajax({
url: url,
dataType:"json"
}).then(function(resp){
//resp = $.parseJSON(resp); /*You dont require this if you have respose as JSON object. Just Specify it in 'dataType'*/
var source = $('#feed-item-template').html();
var template = Handlebars.compile(source);
var context = {
id: post.id,
content: post.text,
author: post.author,
date: $.timeago(post.date),
staff: resp.is_staff === 1 ? true : false
};
return template(context);
});
}
isStaff(url, post).done(function(template){
/*Your compiled template code is available here*/
}).fail(function(jqXHR, textStatus, errorThrown){
console.log("Error:"+textStatus);
});
Note: Be sure to implement error callbacks also. Because you may never know what
went wrong :)
Simple explanation about promise with $.defer:
For understanding i have created the Fiddle similar to your requirement.
Explanation:
Basically Promise is been introduced to attain synchronous execution of asynchronous JS code.
What do you mean by Async or Asynchronous code?
The code that is executed may return a value at any given point of time which is not immediate. Famous example to support this statement would be jquery ajax.
Why is it required?
Promise implementations helps a developer to implement a synchronous code block which depends on asynchronous code block for response,. like in ajax call when i make a request to server asking for a data string, i need to wait till the server responds back to me with a response data string which my synchronous code uses it to manipulate it , do some logic and update the UI.
Follow this link where the author has explained with detailed examples.
PS: Jquery $.defer implements or wraps promise in quite a different way. Both are used for the same purpose.
let basedataset = {}
let ajaxbase = {};
//setting api Urls
apiinterface();
function apiinterface() {
ajaxbase.createuser = '/api/createuser'
}
//setting up payload for post method
basedataset.email = profile.getEmail()
basedataset.username = profile.getGivenName()
//setting up url for api
ajaxbase.url = ajaxbase.createuser
ajaxbase.payload = basedataset;
//reusable promise based approach
basepostmethod(ajaxbase).then(function(data) {
console.log('common data', data);
}).catch(function(reason) {
console.log('reason for rejection', reason)
});
//modular ajax (Post/GET) snippets
function basepostmethod(ajaxbase) {
return new Promise(function(resolve, reject) {
$.ajax({
url: ajaxbase.url,
method: 'post',
dataType: 'json',
data: ajaxbase.payload,
success: function(data) {
resolve(data);
},
error: function(xhr) {
reject(xhr)
}
});
});
}
A solution using async await in js would be like this:
async function getMyAjaxCall() {
const someVariableName = await ajaxCallFunction();
}
function getMyAjaxCall() {
return $.ajax({
type: 'POST',
url: `someURL`,
headers: {
'Accept':'application/json',
},
success: function(response) {
// in case you need something else done.
}
});
}
I need to delay every Ajax call until the previous function (hashcode.Sign()) has been completed. Unfortunately my code doesn't wait until hashcode.Sign() is completed and that messes everything up, since that function creates a new session and it cannot be overwritten in order to things to work. How should I do that? I'm new to Deferred objects so pardon my lack of knowledge.
for (var l = 0; l < fileArray.length; l++) {
var d = new jQuery.Deferred();
d.resolve(openDdoc(fileArray[l].url, $pid, fileArray[l].id));
d.done(function() { console.log('done'); });
}
function openDdoc(url, pid, fid) {
var def = new jQuery.Deferred();
$.when(def).done(function() { console.log('Function completed!'); });
def.resolve($.ajax({
url: '/open-ddoc',
type: 'post',
async: false,
data: {doc: url, pid: pid, fid: fid},
}).done(function (data) {
hashcode.Sign();
}).fail(function(data) {
console.log('this shit failed');
}));
}
The hashcode.Sign() function includes some more Ajax calls and calls to other functions. I can include it later if neede. I know I could resolve all of this with setTimeout(), but that wouldn't be my first choice.
It's not immediately obvious but the sequentiality you seek can be expressed in the form of fileArray().reduce(...).
This tried and tested pattern is outlined here under the subheading "The Collection Kerfuffle". (The whole article is well worth reading).
fileArray.reduce(function(promise, file) {
return promise.then(function() {
return $.ajax({
url: '/open-ddoc',
type: 'post',
async: true, // <<<<< always TRUE!!
data: {doc: file.url, pid: $pid, fid: file.id}
});
}).then(function(data) {
console.log(file.id + ': done');
return hashcode.Sign(); // hashcode.Sign() must return a promise
});
}, $.when()) // <<<<< resolved promise to get the chain started
.then(function() {
console.log('all done');
});
In this pattern, each member of fileArray is visited in turn (synchronously), adding a fresh .then() to a growing promise chain at each visit.
The resolved starter promise, $.when(), causes the constructed chain to start settling, and the ajax calls are made in sequence.
The ajax call doesn't need to be in a separate function, unless it's going to be called elsewhere.
It should be emphasised that hashcode.Sign() (which we understand to be asynchronous) must be written in the right way such that it returns a valid promise. If not, then the settlement process has no way of ensuring its asynchronism(s) to be complete before proceeding.
Im trying to understand promises, but Im hitting a roadblock, I'm trying to query my Parse database for the last ran date object so that ill know when it was ran last. Then pass that date to the next function who can check my movie database for anything after the last time it was called. (I'm doing this to send out push notifications for new fields manually entered into Parse class) then actually send the push. But I'm not understanding the .then and promises, I'm new to JavaScript so any help would be appreciated!
Here is my code i have now.
Parse.Cloud.job("TestSendNewMoviePush", function(request, response) {
var query = new Parse.Query("MovieStatus");
var lastRunDateQuery = new Parse.Query("LastRun");
var lastRunDate;
var newDate;
var newCount = 0;
var installQuery = new Parse.Query(Parse.Installation);
query.greaterThan("updatedAt", lastRunDate);
query.equalTo("CurrentStatus", "Ready");
query.equalTo("imageStatusName", "Green");
installQuery.equalTo("role", "downloader");
lastRunDateQuery.get("d3WeNEwzIu", {
success: function(lastDateRanObj) {
console.log("Got the object " + lastDateRanObj);
var date = new lastDateRanObj.updatedAt;
lastRunDate = lastDateRanObj.updatedAt;
console.log(lastRunDate);
return lastRunDate;
},
error: function(lastDateRanObj, error) {
console.log("Failed to get object");
}
}).then(
query.count({
success: function(count) {
newCount = count;
return newCount
},
error: function(e) {
}
})).then(
Parse.Push.send({
where: installQuery,
data: {
"alert": newCount + " new movie(s) available!",
"badge": "Increment"
}
}, {
success: function() {
response.success("Success");
},
error: function(e) {
response.error("Error:" + e.code);
}
}));
});
lastRunDateQuery.get() returns a Promise object, which you can chain with a then, which itself is a function taking 2 functions as arguments: one that is called if the promise is resolved, and one that is called if the promise is rejected. You use these instead of the success and error parameters:
lastRunDateQuery.get()
.then(function(data) {
// success!!
}, function(error) {
// error :(
});
The functions you passed as arguments to then can themselves return promises, which you may subsequently chain with a then. In the example below I have omitted the error callbacks:
lastRunDateQuery.get()
.then(function(data) {
// data is the result of lastRunDateQuery.get()
return query.count();
})
.then(function(data) {
// data is the result of query.count()
return someOtherThing.someOtherMethodReturningAPromise();
});
.then(function(data) {
// data is the result of someOtherThing.someOtherMethodReturningAPromise()
});
And so on.
I would recommend having a look at the Promises/A+ spec - it's very instructive.
EDIT:
If the chaining concept is a bit confusing just think of it as a shorthand for the following:
var aPromise = lastRunDateQuery.get();
aPromise.then(
function() {}, // promise was resolved -> call this function
function() {}, // promise was rejected -> call this function
);