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;
})
}
Related
I know questions similar to this have been asked many times, but some are old and suggest deprecated solutions, some describe a solution but not in the context of a loop, and none of them directly answers my question. I want to know what is the latest and greatest approach to having a piece of code run after another piece of code is done running a bunch of async calls.
Here is the general structure of the code as I have it:
function fetchProperty(input) {
$.ajax({
url: 'https://jsonplaceholder.typicode.com/todos/1' + input
}).done(function(resp){
return $.parseJSON(resp).title;
});
}
inputValues = [1, 2];
outputValues = [];
$.each(inputValues, function(index, value){
outputValues.push(fetchProperty(value));
});
console.log(outputValues); // will return an empty array
Essentially, I want that last line of code to not be executed until after all of the AJAX calls made inside the $.each() are finished. Obviously, I don't want to use async: false because it is deprecated. What is the best practice on how to defer the console.log until after all of the other deferred AJAX calls are done?
Try to use async await, like in the code below.
The problem seems to be that the fetch is async but the console log is not so it print before it fetch the data
async function fetchProperty(input) {
const resp = await $.ajax({
url: 'http://some.api/' + input + '/foobar'
});
return $.parseJSON(resp).someProperty;
}
inputValues = ['this', 'that'];
outputValues = [];
$.each(inputValues, async function(index, value){
outputValues.push(await fetchProperty(value));
});
console.log(outputValues);
I'm not sure that $.ajax return a thenable (Promise like) object or not. Then I will convert $.ajax to the Promise first, and use await keyword to get the value from the function. I use for...of instead of $.each to this task, because $.each use callback style, then it will hard to make it working with async/await.
function fetchProperty(input) {
return new Promise((resolve) => { // return a Promise
$.ajax({
url: 'https://jsonplaceholder.typicode.com/todos/' + input
}).done(function (resp) {
resolve(resp.title); // resolve here
});
});
}
async function main() {
const inputValues = [1, 2];
const outputValues = [];
for (const value of inputValues) {
outputValues.push(await fetchProperty(value));
}
console.log(outputValues); // the values
}
main();
There's a async call I'm making that queries a database on a service, but this service has a limit of how many it can output at once, so I need to check if it hit its limit through the result it sends and repeat the query until it doesn't.
Synchronous mockup :
var query_results = [];
var limit_hit = true; #While this is true means that the query hit the record limit
var start_from = 0; #Pagination parameter
while (limit_hit) {
Server.Query(params={start_from : start_from}, callback=function(result){
limit_hit = result.limit_hit;
start_from = result.results.length;
query_result.push(result.results);
}
}
Obviously the above does not work, I've seen some other questions here about the issue, but they don't mention what to do when you need each iteration to wait for the last one to finish and you don't know before hand the number of iterations.
How can I turn the above asynchronous? I'm open to answers using promise/deferred-like logic, but preferably something clean.
I can probably think of a monstruous and horrible way of doing this using waits/timeouts, but there has to be a clean, clever and modern way to solve it.
Another way is to make a "pre-query" to know the number of features before hand so you know the number of loops, I'm not sure if this is the correct way.
Here we use Dojo sometimes, but the examples I found does not explain what to do when you have an unknown amount of loops https://www.sitepen.com/blog/2015/06/10/dojo-faq-how-can-i-sequence-asynchronous-operations/
although many answers already, still I believe async/await is the cleanest way.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
and you might need babel
https://babeljs.io/
JS async logic syntax changed from callback to promise then to async/await, they all do the same thing, when callback nests a lot we need something like a chain, then promise come, when promise goes in loop, we need something make the chain more plain more simple, then async/await come. But not all browsers support the new syntax, so babel come to compile new syntax to old syntax, then you can always code in new syntax.
getData().then((data) => {
//do something with final data
})
async function getData() {
var query_results = [];
var limit_hit = true;
var start_from = 0;
//when you use await, handle error with try/catch
try {
while (limit_hit) {
const result = await loadPage(start_from)
limit_hit = result.limit_hit;
start_from = result.results.length;
query_result.push(result.results);
}
} catch (e) {
//when loadPage rejects
console.log(e)
return null
}
return query_result
}
async function loadPage(start_from) {
//when you use promise, handle error with reject
return new Promise((resolve, reject) => Server.Query({
start_from
}, (result, err) => {
//error reject
if (err) {
reject(err)
return
}
resolve(result)
}))
}
If you want to use a loop then I think there is no (clean) way to do it without Promises.
A different approach would be the following:
var query_results = [];
var start_from = 0;
funciton myCallback(result) {
if(!result) {
//first call
Server.Query({ start_from: start_from}, myCallback);
} else {
//repeated call
start_from = result.results.length
query_result.push(result.results);
if(!result.limit_hit) {
//limit has not been hit yet
//repeat the query with new start value
Server.Query({ start_from: start_from}, myCallback);
} else {
//call some callback function here
}
}
}
myCallback(null);
You could call this recursive, but since the Query is asynchronous you shouldn't have problems with call stack limits etc.
Using promises in an ES6 environment you could make use of async/await. Im not sure if this is possible with dojo.
You don't understand callbacks until you have written a rate limiter or queue ;) The trick is to use a counter: Increment the counter before the async request, and decrement it when you get the response, then you will know how many requests are "in flight".
If the server is choked you want to put the item back in the queue.
There are many things you need to take into account:
What will happen to the queue if the process is killed ?
How long to wait before sending another request ?
Make sure the callback is not called many times !
How many times should you retry ?
How long to wait before giving up ?
Make sure there are no loose ends ! (callback is never called)
When all edge cases are taken into account you will have a rather long and not so elegant solution. But you can abstract it into one function! (that returns a Promise or whatever you fancy).
If you have a user interface you also want to show a loading bar and some statistics!
You must await for the server response every time. Here a encapsulated method
var query = (function(){
var results = [];
var count = 0;
return function check(fun){
Server.Query({ start_from: count}, function(d){
count = d.results.length;
results.push(d.results);
if (d.limit_hit && fun) fun(results);
else check(fun);
});
};
})();
// Call here
var my_query = query(function(d){
// --> retrive all data when limit_hit is true)
});
You can use a generator function Generators to achieve this
For POC:
some basics
- You define a generator with an asterick *
- it exposes a next function which returns the next value
- generators can pause with yield statement internally and can resume externally by calling the next()
- While (true) will ensure that the generator is not done until limit has reached
function *limitQueries() {
let limit_hit = false;
let start_from = 0;
const query_result = [];
while (true) {
if (limit_hit) {break;}
yield Server.Query(params={start_from : start_from},
callback=function* (result) {
limit_hit = result.limit_hit;
start_from = result.results.length;
yield query_result.push(result.results);
}
}
}
So apparently, the generator function maintains its own state. Generator function exposes two properties { value, done } and you can call it like this
const gen = limitQueries();
let results = [];
let next = gen.next();
while(next.done) {
next = gen.next();
}
results = next.value;
You might have to touch your Server.Query method to handle generator callback. Hope this helps! Cheers!
I'm trying to learn about what the promise is and how to convert callback to promise. While I'm converting my code to promise I got very confused about the ref. I would very appreciate it if you show me how to convert this code as a simple example.
database.ref('/users').on("child_added").then(function(snap){
var subData = snap.val();
database.ref('/subs/' + subData.subid + '/pri/' + snap.key).once("value").then(function(userSnap) {
var userData = userSnap.val();
subData.name = userData.name;
subData.age = userData.age;
database.ref('/subs/' + subData.subid).once("value",function(subDSnap) {
var subDData = subDSnap.val();
subData.type = subDData.type;
database_m.ref('/users/' + snap.key).set(subData);
});
});
});
A Promise is not a replacement for every type of callback; rather it's an abstraction around one particular task that will either succeed once or fail once. The code you're converting looks more like an EventEmitter, where an event can occur multiple times, so replacing .on('child_added', ...) with a Promise implementation is not a good fit.
However, later on, you have a .once(...) call. That's a bit closer to a Promise in that it will only complete once. So if you really wanted to convert that, here's what it could look like:
function get(database, url) {
return new Promise(function (resolve, reject) {
database
.ref(url)
.once('value', resolve)
.once('error', reject);
});
}
database.ref('/users').on("child_added", function(snap) {
var subData = snap.val();
get(database, '/subs/' + subData.subid + '/pri/' + snap.key)
.then(function(userSnap) {
var userData = userSnap.val();
subData.name = userData.name;
subData.age = userData.age;
return get(database, '/subs/' + subData.subid);
})
.then(function(subDSnap) {
var subDData = subDSnap.val();
subData.type = subDData.type;
database_m.ref('/users/' + snap.key).set(subData);
})
.catch(function (err) {
// handle errors
});
});
});
I am not sure I understand the question and if your "on" is returning a promise or just listening, but in your code you have nested '.then', which is not the common way to deal with promises and I am not sure that's what you wanted to achieve here.
You could do (assuming that the on function returns a promise, which I doubt)
database.ref('/users').on("child_added")
.then(function(snap){/* do something with the first part of your snap function*/})
.then (results => {/* do something else, such as your second ref call*/})
.catch(error => {/* manage the error*/})
To learn about promises, there are many examples online but what I really liked is the promise tutorial at google, with this nice snippet that explains it
var promise = new Promise(function(resolve, reject) {
// do a thing, possibly async, then…
if (/* everything turned out fine */) {
resolve("Stuff worked!");
}
else {
reject(Error("It broke"));
}
});
then, once you have the function that returns this promise, you can start doing
.then(...)
.then(...)
.then(...)
.catch(...)
I have a javascript file where I'm grabbing a user's geolocation. I've wrapped it in a promise to make sure that it returns back coordinates.
geoLocation.js:
const getGeoLocation = () => {
return new Promise(function(resolve, reject){
function positionSuccess(position){
resolve(position);
}
function positionError(error){
reject('ERROR: ' + error.code + ': ' + error.message);
};
navigator.geolocation.getCurrentPosition(positionSuccess, positionError);
});
};
async function userCoords() {
const response = await getGeoLocation();
}
module.exports = userCoords;
From here I'm importing the async userCoords function into a Main.js react component. In it's render method I'm asking it to console.log(userCoords()), but what I'm getting in the console is:
It looks like it's still waiting to finish with my getGeoLocation promise. What should I be doing to make sure that getGeoLocation is finished completely and that I can pass on it's return coordinates to my Main.js component?
Thank you!
An async function will always return a promise, even if it is already settled. You must use promise.then or await. If you can make your render function async, then you can write:
console.log(await userCoords());
A function being async is poisonous, meaning that any function calling an async function (and expecting a settled value) must be itself async. This is particularry painful when programming user interface, because UI codes are inherently synchronuous. Showing data which comes from an asynchronouos datasource is hard to do well and demand careful engineering.
Maybe the simplest thing you can do is to poll the user coordinates independently from your render cycle:
var coords_promise = null;
var coords = null;
function render() {
if (!coords_promise) {
coords_promise = userCoords().then(function(newCoords) {
coords = newCoords;
coords_promise = null;
});
}
console.log(coords);
}
setInterval(render, 20);
This piece of code will start fetching the coordinates at invocation to the render function. This means the first few cycles of the render function will print the initial value of the coords variable, but after then it will use the newest settled return value of userCoords(). This code also makes sure that no fetching is made if no call was made to the render() function, and no parallel invocations will be made to userCoords().
One of the posts in the comments section of this typescript blog post says:
If I have to wait until 2.0 for ES6 generator support, I'm just gonna
stick with Traceur. Generators are a big deal, they give you
async/await support today using libraries such as Koa, Co, Bluebird,
etc.
Async/await keywords would allow applications to retain a logical structure that resembles synchronous code. How would one use a generator to accomplish something similar? For example, how would you use a generator in conjunction with an ajax call to produce synchronous style code that avoids using callbacks?
You just have to abstract that with an helper function.
Assuming jQuery:
function ajax(type, url, data){
$.ajax({
url: url,
data: data,
type: type
})
.done(function(data) {
iterator.next(data);
})
.fail(function() {
iterator.throw();
});
}
var get = ajax.bind(null, 'GET');
var post = ajax.bind(null, 'POST');
var put = ajax.bind(null, 'PUT');
var patch = ajax.bind(null, 'PATCH');
var del = ajax.bind(null, 'DELETE');
function *asyncGet() {
var URL = 'https://api.stackexchange.com/2.2/answers?order=desc&sort=activity&site=stackoverflow'
var data = yield get(URL);
console.log(data);
}
var iterator = asyncGet();
iterator.next();
Another example using setTimeout:
function delayReturn(time, val){
setTimeout(function(){
iterator.next(val);
}, time);
}
var delayReturn1s = delayReturn.bind(null, 1000);
function *main() {
console.log(yield delayReturn1s('Lolcat'));
}
var iterator = main();
iterator.next()
Of course you can abstract the iterator passing with something like this:
var async = function(generator){
var resume = function(err, data){
if (err) iterator.throw();
iterator.next(data);
}
var iterator = generator(resume);
iterator.next();
}
Then you can simply:
function ajax(type, url, data, cb){
$.ajax({
url: url,
data: data,
type: type
})
.done(function(data) {
cb(null, data)
})
.fail(function() {
cb(arguments);
});
}
var get = ajax.bind(null, 'GET');
var post = ajax.bind(null, 'POST');
var put = ajax.bind(null, 'PUT');
var patch = ajax.bind(null, 'PATCH');
var del = ajax.bind(null, 'DELETE');
async(function *(resume) {
var URL = 'https://api.stackexchange.com/2.2/answers?order=desc&sort=activity&site=stackoverflow'
var data = yield get(URL, null, resume);
console.log(data);
});
For example, how would you use a generator in conjunction with an ajax call to produce synchronous style code that avoids using callbacks?
From Beginning Node.js :
As a thought experiment imagine the following, a way to tell the JavaScript runtime to pause the executing of code on the await keyword used on a promise and resume only once (and if) the promise returned from the function is settled.
// Not actual code. A thought experiment
async function foo() {
try {
var val = await getMeAPromise();
console.log(val);
}
catch(err){
console.log('Error: ',err.message);
}
}
When the promise settles execution continues, if it was fulfilled then await will return the value, if it's rejected an error will be thrown synchronously which we can catch. This suddenly (and magically) makes asynchronous programming as easy as synchronous programming. Three things are needed:
Ability to pause function execution.
Ability to return a value inside the function.
Ability to throw an exception inside the function.
The good news is this magic is very real, and possible to try today. The syntax will be slightly different, because the technology we will be using wasn't designed only for this. It is possible because of JavaScript generators, a technology coming with ECMAScript 6 that you can use today.
Generators allow you to pause a function execution (using the yield keyword) return a value inside (using the .next(val) function) and throw and exception inside (using the .throw(err) function). These APIs are explained in the book and you can also view them on generator documentation. Still you should get the point / power even without understanding the exact API as you now know the correlation.
From Thomas Hunter's blog post The long road to Async/Await in JavaScript
Stage 3: Generators/Yields (ES6) shows an example of using ES6 generators to do async JavaScript. However, this is for demonstration only and most likely you don't want to use this technique. Use async/await instead with an ES7 (experimental) to ES5 traspiler such as Babel or TypeScript.
var generator = publishLevel(12, {data: true});
generator.next().value.then(function(user) {
return generator.next(user).value.then(function(can_create) {
return generator.next(can_create).value.then(function(level_result) {
console.log(level_result);
});
});
});
function * publishLevel(user_id, level_data) {
var user = yield getUser(user_id);
var can_create = yield canCreate(user);
if (!can_create) {
return null;
}
var level = yield saveLevel(user, level_data);
return level;
}
function getUser(user_id) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve({
id: user_id,
nickname: 'tlhunter'
});
}, 100);
});
}
function canCreate(user) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(user.id === 12);
}, 100);
});
}
function saveLevel(user, data) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve({
id: 100,
owner: user.nickname,
data: data
});
}, 100);
});
}