How can I use fetch in while loop - javascript

My code is something like this:
var trueOrFalse = true;
while(trueOrFalse){
fetch('some/address').then(){
if(someCondition){
trueOrFalse = false;
}
}
}
But I can not issue the fetch request. It seems like while loop is schedule many fetches into next tick. But never skip to next tick.
How can I solve the problem?

while(true) creates an infinite loop, which will attempt to call fetch infinitely many times within a single "tick". Since it never finishes issuing new fetch calls, it never gets to the next tick.
This function is very CPU-intensive and will probably lock up the entire page.
What's the solution?
What you are probably trying to do is keep fetching until the result satisfies some condition. You can achieve that by checking the condition in the then callback, and re-issuing the fetch if it is false:
var resultFound = false;
var fetchNow = function() {
fetch('some/address').then(function() {
if(someCondition) {
resultFound = true;
}
else {
fetchNow();
}
});
}
fetchNow();
This way, instead of
fetch!
fetch!
fetch!
fetch!
...
...the behavior is going to be
fetch!
wait for response
check condition
if false, fetch!
wait for response
check condition
if true, stop.
...which is probably what you expected.

Now with async/await we can use awesome while loops to do cool stuff.
var getStuff = async () => {
var pages = 0;
while(true) {
var res = await fetch(`public/html/${pages ++}.html`);
if(!res.ok) break; //Were done let's stop this thing
var data = await res.text();
//Do something with data
};
console.log("Look ma! I waited!"); //Wont't run till the while is done
};

while loop is sync where fetch is async in nature, so while won't wait for fetch async operation to complete and going to next iteration immediately.
You can achieve this synchronously like following:
function syncWhile(trueOrFalse){
if(trueOrFalse) {
fetch('some/address').then(){
if(someCondition){
trueOrFalse = false;
}
syncWhile(trueOrFalse);
}
}
}
syncWhile(true);

The while loop fires off all the fetches before any one of them will reach the then(), so a while loop is incorrect here, even useless I would say.
You need to make the then() responsible for whether to continue fetching or not.
It also seems that your then()-syntax is wrong (maybe just an error editing the example). Furthermore you can omit the boolean helper variable (unless perhaps you need it in some other place).
function fetchUntilCondition(){
fetch('some/address').then(function(response){
if(!someCondition) {
fetchUntilCondition(); // fetch again
}
});
}
fetchUntilCondition();

Related

Repeatedly call a function that returns a promise until one of them contains a specific response

Hello I need to call a REST function with an ID, that returns a promise in React.js. This function will at some point contain a certain value in its response when called . Until another service has processed an initial request this value will be null.
This is what I have done so far:
while(myVariable){
myfunction(myID).then( (response) => {
if(response['value'] != null
myVariable = false;
}
});
}
The problem with this code is that the while loop is called as fast as possible and thus completely utilises the computer. So I need a function that allows me to poll for a result by an ID until the response of one of the function calls contains a valid response.
I have tried the following method but without success, because I don't have a fixed number of runs:
Wait promise inside for loop
Thanks in regards.
As you state, the problem is that the while loop runs eagerly, not waiting for each promise to resolve.
One way to solve that is to use recursion. Recursion gives you more control over when exactly you want to 'loop' next:
let getValue = () => {
myFunction(myID).then(response => {
if (response['value'] === null) {
setTimeout(getValue);
} else {
// here you know the other service has processed the initial request
}
});
};
First I wrapped the whole thing in a function called getValue. Note that this function is only called again after the promise resolves. (The call to setTimeout is a trick to use recursion without consuming the stack.) If this still runs too quickly, pass an additional parameter of 100 or so to the setTimeout invocation.
Alternatively, you may be able to use async/await similarly to the link you shared. I'm no expert on async/await but it should work the same with while loops as with for loops, judging by this and this.
You can use the async function with await.
I also use a delay function to delay each call to the myfunction().
While you get a response, you can break the while loop.
const delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms));
async function main() {
const myID = 1;
let response;
while (true) {
response = await myfunction(myID);
if (response["value"] != null) {
break;
}
await delay(5000);
}
//do Something once you get the response here below:
}
main();

How do I know that the loop callback is complete?

allDoc.map(function(stage){
for(let key of stage.layerItem){
//key.toImage is a library, so it only supports callback.
key.toImage({
callback(img) {
addPhoto(key.attrs)
}
});
}
})
// Run when addPhoto is done
console.log("done")
async function addPhoto(params){
const stored = await s3.upload(params).promise()
return stored.Location
}
When the addPhoto function is completed, how do I proceed to the next code?
First wrap the toImage function in one that returns a promise then use Promise.all to wait for them all to settle.
(Community Wiki because this is close to being a duplicate question but has two distinct steps each of which has their own duplicate).
One of the solutions I found for waiting .map() to be complete, is putting a Promise.all().
Your code should look like this:
const promisse = allDoc.map(function(stage, index){
// Your loop
})
await Promise.all(promisse);
console.log('done');
Keep in mind that if your allDoc.map is inside another function, the upper function must be async.

Trying to understand why this "while" loop doesn't work as expected in React/JSX/node?

Simple problem that I haven't found a solution to. Trying to read a variable found inside a Firebase document "NumberOfQuestionsInList" starting with list 1 and going to the value of "Maximum". At first attempted a simple solution like below, however the while loop doesn't act in synchronous order and therefore the while loop continues increasing without ever calling the .get() function:
let booleanValue = false;
while(booleanValue===false){
const QListDirectory = firestore.collection("content").doc(`List${i}`);
QListDirectory.get().then((docSnapshot) => {
if (docSnapshot.exists) {
QListDirectory.onSnapshot((doc) => {
let NumberInList= doc.data().NumberOfQuestionsInList;
//Series of if, else if statements depending on the value of the "NumberInList".
//These if(), else if() statements contain commands to change "booleanValue" to true.
//when other conditions are met.
});
}
});
//Iterate over i, moving from List1, to List2 and so on....
i=i+1;
}
What I end up observing is just a while loop that continues to increase i by 1 and never appears to actually .get() and of the Firebase document information. Any clarity as to why a while loop like this isn't allowed would be greatly appreciated.
What is happening here is that you have the get of the promise that reads to the firestore taking some time and since you have more code to run (the i=i+1;) outside of the promise you are just skipping the portion of the .then((docSnapshot) completely. Here you can see the anatomy of a promise
What you should do is first add a catch for that promise which is a really important part of a promise, and then move that i=i+1; inside of your your promise and also include it in your catch section so in case of an exception the loop continues.
Another possible solution here is to change from promises to async and declare your function as an async function, then you can use something like this
async function myloop() {
const QListDirectory = firestore.collection("content").doc(`List${i}`);
const docSnapshot = await QListDirectory.get();
return docSnapshot.get("NumberOfQuestionsInList");
}
This may not be the exact code that you need since you have multiple things to do that are redacted but this gives you an idea of another possible solution

Execute Promises (or Deferreds) one after the other and be able to interrupt at any time

So, I'm having a problem. I need to make potentially hundreds of http calls and they must be done one after the other. I also need to be able to interrupt the whole process at any time.
The solution I'm using right now is this one, but it doesn't allow me to properly interrupt the "chain" (I use jQuery and Deferreds because I haven't learned to replace them with Promises yet):
function doAnHttpCall() {
return $.post('http://the.domain/theendpoint', { theParam: this });
}
var anArrayOfParams = ['param1', 'param2', 'param3'];
var p = $.Deferred();
setTimeout(p.resolve.bind(p), 0);
var promise = anArrayOfParams.reduce(function(prev, cur) {
return prev.then(doAnHttpCall.bind(cur));
}, p)
.done(function() {
console.log('all done.');
});
(I've found this solution here)
The problem here is that the only way to sort-of break out of the reduce function is by modifying the array you're looping through and check for a particular value and always return immediately when you find it, instead of executing the "doAnHttpCall" method (in this case). This would still make me loop over potentially hundreds of elements of the array instead of just interrupting the process, which is very ugly.
There must be a better way to do this. Or do I really need to use a function that calls itself with the next element to process when an http call has finished? It sounds "bad-practice-y".
Thanks for the help.
You would use async/await:
const request = param => $.post('http://the.domain/theendpoint', { param });
(async () => {
for(const param of ['param1', 'param2', 'param3']) {
let result = await request(param);
if (result === 'particular value') break;
}
console.log('all done!');
})();

What is the correct pattern with generators and iterators for managing a stream

I am trying to figure out how to arrange a pair of routines to control writing to a stream using the generator/iterator functions in ES2015. Its a simple logging system to use in nodejs
What I am trying to achieve is a function that external processes can call to write to a log.I am hoping that the new generator/iterator functions means that if it needs to suspend/inside this routine that is transparent.
stream.write should normally return immediately, but can return false to say that the stream is full. In this case it needs to wait for stream.on('drain',cb) to fire before returning
I am thinking that the actual software that writes to the stream is a generator function which yields when it is ready to accept another request, and that the function I provide to allow external people to call the stream is an interator, but I might have this the wrong way round.
So, something like this
var stopLogger = false;
var it = writer();
function writeLog(line) {
it.next(line);
})
function *writer() {
while (!stopLogger) {
line = yield;
if(!stream.write) {
yield *WaitDrain(); //can't continue until we get drain
}
}
});
function *waitDrain() {
//Not sure what do do here to avoid waiting
stream.on('drain', () => {/*do I yield here or something*/});
I found the answer here https://davidwalsh.name/async-generators
I have it backwards.
The code above should be
var stopLogger = false;
function *writeLog(line) {
yield writer(line)
})
var it = writeLog();
function writer(line) {
if (stopLogger) {
setTimeout(()=>{it.next();},1};//needed so can get to yield
} else {
if(stream.write(line)) {
setTimeout(()=>{it.next();},1}; //needed so can get to yeild
}
}
}
stream.on('drain', () => {
it.next();
}
I haven't quite tried this, just translated from the above article, and there is some complication around errors etc which the article suggests can be solved by enhancing the it operator to return a promise which can get resolved in a "runGenerator" function, But it solved my main issue, which was about how should the pattern work.

Categories