Call API untill certain response - javascript

I really struggle with async functions in Javasctipt. Here I have async function that calls api and saves the result. It is working fine but I need to make a loop now so this api is called untill certain condition is met. I understand that it needs to be done using await but I just can't figure out how exactly.
I tried setting up if statement and then doing something like "if condition is not met" setTimeout(getResults()); (repeat the call to async function).
async getResults() {
try {
const res = await axios(`https://blablabla`);
this.result = res.data.info;
} catch (error) {
alert(error);
}
}

async getResults() {
try {
let i = 100;
while(i-->=0){
const res = await axios(`https://blablabla`);
this.result = res.data.info;
if(this.result == 'some process finished')
{
return this.result;
}
//else retry
}
} catch (error) {
alert(error);
}
just use some cycle like while(true). And repeat body of cycle until your conditions are met

Related

Async button in electron call an async function

I have an async button on my html site. When I click the button, it should call a function, which waits for a string from my Arduino and then returns the string to the other function. If the data is saved, it should display the string on the console.
Here is the code:
async function DataReceived()
{
await Port.parser.on('data',function(data){
return data;
});
}
table.send.onclick = async () =>{
let data = await DataReceived().then(()=> {
if (data != "")
{
console.log(data);
}
else
{
console.log("too late");
}
})
Normally the function should wait for DataReceived(), but it jumps directly in the if condition and sends me back too late.
What did I do wrong? I hope you can help me.
The DataReceived() function just registers the on-event, but doesn't wait for the data to occur.
You may consider moving this registration straight into the onclick function and add your logic to the function body of the Port.parser callback
Thanks to Felix King. You got me on the right way. The Port.parser.on doesn't return a promise. I had to enclose it in new Promise for it work.
a post with the answer already exist here:
async/await using serialport in node.js
My code looks now like this now:
function DataReceived()
{
return new Promise(function(resolve, reject) {
Port.parser.on('data', (data) => {
console.log (data)
resolve(data)
})
});
}
table.send.onclick = async () =>{
let data;
data = await DataReceived();
if (data != "")
{
console.log(data);
}
else
{
console.log("too late");
}
}

Async Function Inside Async Function

I am trying to get Data over my API-Endpoint and want to populate my page with the data.
I know that I have to use async/await to be sure that the variable was filled. Therefore I wrote the following lines and want to know that it is possible to write an async function inside an async function. It is working but I am not sure if it is the correct way:
async function getRoom(id) {
const response = await fetch('/api/zimmer/' + id);
if (!response.ok) {
const message = `An error has occured: ${response.status}`;
throw new Error(message);
}
const rooms = await response.json();
console.log(rooms);
return rooms;
}
async function getSelectedItem() {
var e = document.getElementById("Objekt");
if (e.value > 0) {
zimmer_select.disabled = false;
var response = await getRoom(e.value);
console.log(response);
response.forEach(function(element) {
console.log(element);
});
} else {
zimmer_select.disabled = true;
}
console.log(e.value);
}
I think it is fine to use it this way actually.
If you think about, sometimes you have to use async inside async. What if you have some async function which fetches data and you have to loop over that data and await for something. If you loop it with for each you won't be able to use await, so you should put async before it. So as long it's clean and have no other way, I think it is fine to use async inside async.

How to use async/ await for a 'for statement' only

I want to use async function 'submitToTheOthers' and I want from let items = [] it will run after 'submitToTheOthers' is done. But it didn't wait until 'submitToTheOthers' is done. Probably it is because no await in 'submitToTheOthers'. Therefore, I want to make for statement in 'submitToTheOthers' as await like, but I don't know how.
I could use await at 'updateGeneralInfoToOther', but then I couldn't run
updateGeneralInfoToOther for all servers at the almost same time, and if one of server has a error then I couldn't run from the server in the for statement. I want use await or promise like something for statement or little larger range.
What will be good way to make it wait 'submitToTheOthers'?
ps)I hope get to know older syntax because I likely need to use it on internet explorer XD XD TT
Settings.vue
<template>
<div>
<button class="point" #click="submitTotalServer()">submitTotalServer</button>
</div>
</template>
<script>
import {
updateGeneralInfoToOther,
} from '#/api/settings'
export default {
data() {
return {
serverNames: ['a server', 'b server'],
idxs: [0,1],
serverFullAddress: ['http://localhost:12345','http://randomUrl:12345' ]
serverResCheck: [],
}
},
methods: {
async submitTotalServer() {
await this.submitToTheOthers() // this function
let items = [] // here
for(let i=0; i< this.idxs.length ; i++){
if(!this.serverResCheck[i]){
items.push(this.serverNames[i])
}
}
if(items == []){
alert('saved in all servers')
}else{
alert(`[${items}] Error`)
}
},
async submitToTheOthers(){
let data = {
data : 'test',
}
for (let i=0; i< this.idxs.length ;i++){
updateGeneralInfoToOther(1, data, this.serverFullAddress[i]').then((res) => // axios function
{
if (res.status == 200) {
this.serverResCheck[i]= true
}else{
this.serverResCheck[i]= false
}
})
}
}
},
</script>
api/settings.js
// ...
export function updateGeneralInfoToOther(id, data, serverAddress) {
axios.defaults.timeout = 5000;
data.serverAddress = serverAddress
return axios.post(`/api/settings/generalToOther/${id}`, data)
}
// ...
The crux of the problem is that your for loop is not awaiting for the promise returned by updateGeneralInfoToOther() to resolve. To get your code to work, it is a quick fix of forcing the loop to await, i.e.:
for (let i=0; i< this.idxs.length ;i++) {
await updateGeneralInfoToOther(1, data, this.serverFullAddress[i]).then((res) => {
if (res.status == 200) {
this.serverResCheck[i]= true
} else {
this.serverResCheck[i]= false
}
})
}
However, this solution is not ideal, in the sense that you have to await each request individually: there is really no reason to do that. A better solution will be to simply perform all these async operations together, and then wait for all of them to be resolved. Your submitToOthers() function will return instead an array of promises:
return this.idx.map((_entry, i) => {
return updateGeneralInfoToOther(1, data, this.serverFullAddress[i]).then((res) => {
if (res.status == 200) {
this.serverResCheck[i] = true
} else {
this.serverResCheck[i] = false
}
})
});
Then, it is just a matter of using Promise.all() to wait for this array of promises to be resolved:
async submitTotalServer() {
await Promise.all(this.submitToTheOthers());
// Rest of the logic
// ...
}
maybe this is what u need
for await
async function test() {
let array=[1,2,3,4,5]
for await (const elem of array) {
console.log(elem);
}
}
By marking a function async you're telling it: if you run into an await, do not proceed to rest of code until the awaited promise has returned (resolved or rejected). But inside your submitToTheOthers function, you're not using await at all, so the requests are made all at once and the function returns without waiting for them.
Although placing an await in front of each call would solve your problem, inside the loop, each iteration would wait for the previous one to finish, which will surely take a lot longer than if you sent all the requests at once. From what you've shown, it looks like they could be run in parallel.
For this, you could use Promise.all() which is designed to handle multiple separate promises in parallel. The following should work, without the need to make any other change to your code:
submitToTheOthers() {
return Promise.all(
this.idxs.map((_, i) => updateGeneralInfoToOther(
1,
{ data: 'test' },
this.serverFullAddress[i]
)
.then(r => {
this.serverResCheck[i] = r.status === 200;
return r;
})
.catch(e => {
this.serverResCheck[i] = false;
return e;
})
));
}
Even though you've marked async submitToTheOthers, you aren't actually making it behave like an async function.
Inside that function, I assume it's this line that's async: updateGeneralInfoToOther(1, data, this.serverFullAddress[i]'). You call this function in a loop, but the problem is that you don't await it, nor do you return the promise.
You'll need to put await in front of it, but that will make it wait for each one to process the next one in the loop. The alternative is storing each promise in an array and then doing await Promise.all(promises) at the end of the function.

Promise on change event

I'm trying to understand how Promise API works.Currently, my idea is when a switch button is pressed/clicked to log some message or do some AJAX, depends on the situation.Which is a perfect example for chaining.Unfortunately, then does not work as expected and it logs the message on page load not on change event
const userID = $(".options").data('id');
let isEnabled = $("#statusToggler").data('status');
function showStatus() {
if(isEnabled) {
$('#statusToggler').bootstrapToggle('on', true);
} else {
$('#statusToggler').bootstrapToggle('off', true);
}
}
async function toggleStatus() {
return new Promise(function (resolve, reject) {
resolve(function () {
$("#statusToggler").on('change', function () {
let status = $("#statusToggler").prop('checked');
if (status === true) {
$("#statusToggler").data('status', 1);
$('#statusToggler').bootstrapToggle('on', true);
} else {
$("#statusToggler").data('status', 0);
$('#statusToggler').bootstrapToggle('off', true);
}
})
});
});
}
async function ajaxModifyStatus() {
await toggleStatus()
.then(function () {
console.log("Do something when toggle is pressed/clicked");
});
}
showStatus();
ajaxModifyStatus();
On first scan your promise syntax looks fine. The functions are both being called at the bottom of the script which is why they're running on page load.
If you want to have them run on an event you want to give the event the function like it's an argument like ajaxModifyStatus rather than ajaxModifyStatus().
Sorry I haven't used jQuery in ages so cant remember how to write it. Here's how you might do with normal js...
let element = document.getElementById('myid')
element.addEventListener('change', ajaxModifyStatus)
// Or...
element.addEventListener('change', async() => {
let result
try {
result = await ajaxModifyStatus()
} catch (err) {
console.log(err)
}
// Do something with result...
})
As the previous comments mentioned you don't necessarily need promises for this to work unless you actually want the result of the Ajax function.
Note I've actually written this as async await syntax because it's easier on my phone. Just replace that code with the promise if you want to do it that way.

Nested multilayered async/await doesn't seem to wait

I have a piece of code simplified version of which looks like this:
let dataStorage1; //declare global vars for easier access later on
let dataStorage2;
let stopLight = true; //this variable is used to 'mark' an iteration as successful (= true) or
//failed (= false) and in need of a retry before continuing to the next
//iteration
let delay = 2000; //the standard time for a delay between api calls
async function tryFetch() {
try {
dataStorage1 = await api.fetch('data_type_1'); //fetch needed data trough api, which
//fills the global variable with an
//object
dataStorage2 = await api.fetch('data_type_2'); //do the same
stopLight = true; //change the value of stopLight to true, thus marking this iteration
//as successful
} catch (err) {
console.log(err);
stopLight = false;
}
}
async function fetchData() {
stopLight = true; //change the stopLight to default before execution
await tryFetch(); //fetch data and assign it to variables
//this section is needed for retrial of fetching after a 2s delay if the first attempt was
//unsuccessful, which is repeated until it's either successful or critical error occurred
while (stopLight == false) {
setTimeout(async () => await tryFetch(), delay);
}
}
(async function main() {
await fetchData(); //finally call the function
setTimeout(main, delay); //repeat the main function after 2s
})();
As you can see, self-executing, pseudo-recursive main() calls for await fetchData(), then fetchData() calls for await tryFetch() and finally tryFetch() calls for await api.fetch('~'), as it's defined in the api.
However, once I started the script and paused it after a couple of iterations, I noticed that both dataStorage1 and dataStorage2 remain undefined. If I go through the code step by step in debugger, what happens is that the execution starts at the beginning of fetchData(), moves to the await tryFetch(); line, skips it, and then goes onto the next iteration.
For the reference, if I call dataStorage1/2 = await api.fetch(`~`); in the body of main() directly without any nesting, it works perfectly (unless error occurs, since they are not handled properly).
So, my question is what have I missed?
Indeed, if in an async function you call setTimeout you cannot expect it to perform an await on anything that relates to the callback passed to setTimeout. The call to setTimeout returns immediately, and your while loop is effectively a synchronous loop. It is a so called "busy loop" -- blocking your GUI as it potentially will loop for thousands of times.
As a rule of thumb, use setTimeout only once: to define a delay function, and then never again.
Also avoid using a global variable like stopLight: this is bad practice. Let the async function return a promise that resolves when this is supposed to be true, and rejects when not.
// Utility function: the only place to use setTimeout
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
async function tryFetch() {
try {
let dataStorage1 = await api.fetch('data_type_1');
let dataStorage2 = await api.fetch('data_type_2');
return { dataStorage1, dataStorage2 }; // use the resolution value to pass results
} catch (err) {
console.log(err);
// retry
throw err; // cascade the error!
}
}
async function fetchData() {
while (true) {
try {
return await tryFetch(); // fetch data and return it
} catch (err) {} // repeat loop
}
}
(async function main() {
let intervalTime = 2000; //the standard time for a delay between api calls
while (true) { // for ever
let { dataStorage1, dataStorage2 } = await fetchData();
// ... any other logic that uses dataStorage1, dataStorage2
// should continue here...
await delay(intervalTime); //repeat the main function after 2s
}
})();
I think the problem is in this line: setTimeout(async () => await tryFetch(), delay);. The await statement inside the callback makes the promise returned by that callback wait, not the whole function. So async () => await tryFetch() is a function that returns a promise, but nothing waits for that promise to complete.
Try replacing that code with something line
await new Promise((resolve) => setTimeout(resolve, delay));
await tryFetch();

Categories

Resources