Make Synchronus Api inside for Loop - javascript

I have an array of Objects/String.
this.addtenant().subscribe(r => {
let a = ['plant1', 'plant2'];
for (let b of a) {
this.myService.saveAllData(b).subscribe(r => {
console.log("result" r);
});
}
});
and getAllData is in myservice file which returns an Observable.
saveAlldata(b) {
this.http.post(url,b);
}
The problem is since i am using subscribe the call is being asynchronus, i want to have it something like:
first "plant1" post call has to finish, and then plant2 has to be made. In simple words synchronus call.

I think you should use async/await for synchronous calls.
Here is one of the tutorial:
https://www.techiediaries.com/javascript-async-await-tutorial/
A sample code demo would be something like:
responseArr: any[] = [];
func1()
let x = [1,2,3];
func2(x);
}
async func2(arr) { // make the function async
for(let i=0; i<arr.length ; i++){
const response: any = await this.myService(arr[i]); // wait on each service call. This will give synchronous behaviour
this.handleResponse(response); // handle response in a function if there is common functionality in each response
}
reuturn responseArr; // return the final response
}
handleResponse(response) {
responseArr.push(response);
}

I have found a solution by myself. You can use an async await inside a loop, since forEach is not promise aware and cannot be used with Promise. Here is the final code:
this.addtenant().subscribe(async r => {
let a = ['plant1', 'plant2'];
for(let index=0; index=a.length; index++)
await this.myService.saveAllData(b).toPromise().then(r => {
console.log("result" r);
});
}
}
});
As await works with Promise, so toPromise will replace subscribe()

Related

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.

How do I make processData function wait for the results from my getData function in Angular?

My getData function makes an api call, then puts each returned object into an array. The array is then returned. I now need my processData function to await the results from the getData function and then further process it. Currently I dont get any results when I console.log(cleaningData)What am I doing wrong with async/await? What am I missing?
getData() {
var dataBucket = []
this.https.get('https:******FAKEURL*******').subscribe((response: any) => {
console.log(response.data)
for(let i = 0 ; i < response.data.length ; i++) {
dataBucket.push(response.data[i])
}
});
console.log(dataBucket);
return dataBucket;
}
async processData() {
let cleaningData = await this.getData();
console.log(cleaningData);
//do something with cleaningData
}
In angular you normally follow another logic for async functions. You declare what should happen when an asynchronous function returns with a subscription. So what should happen when it returns start from your subscription block of code (not from somewhere else where you wait for your asunchronous function)
getData() {
this.https.get('https:******FAKEURL*******').subscribe((response: any) => {
var dataBucket = [] <-----this should be here declared
console.log(response.data)
for(let i = 0 ; i < response.data.length ; i++){
dataBucket.push(response.data[i])
}
this.processData(response) <-------you call that here
});
}
processData(response: any){ <-----you don't need async and await
// <----- here you can do anything with the response from getData()
console.log(cleaningData);
//do something with cleaningData
}

Javascript await inside a loop

I am trying to work with an api where I have to send a request for each item in a list.
However, I see that the loop doesn't seem to wait for every request, i.e, the loop doesn't work as expected. Here's the code below
getInfo = async () => {
const mylist = ["item1","item2","item3","item4","item5","item6","item7"]
const responses = []
const len = mylist.length
for (let i = 0; i < len; i++) {
//console.log("inside loop")
await axios.get("some_url/"+mylist[i])
.then(res => {
responses.push(res.data)
})
}
When I run the program, all the console.log("inside loop") executes immediately without waiting for the request to be complete.
How can I modify the code so as to wait for each response to be completed before updating the for loop counter variable?
You could try re-arranging the code to something like this. But using a Promise.all with Array.prototype.map would be more idiomatic solution for the problem.
await the async call (remove unnecessary .then call) and then console.log
getInfo = async () => {
const mylist = ["item1","item2","item3","item4","item5","item6","item7"]
const responses = []
const len = mylist.length
for (let i = 0; i < len; i++) {
responses.push((await axios.get("some_url/"+mylist[i])).data)
console.log("inside loop")
}
}
Internally, await is translated into a Promise chain. Since the for loop can't be transformed into a Promise-continuation, you'll need to convert it to a Promise-based construct.
Depending on what you want to achieve there are multiple ways to go about it.
Constructing the responses array could be done with a map statement.
const promises = mylist.map(item => {
return axios.get("some_url/"+item).then(res => { return res.data; })
});
const data = await Promise.all(promises);
No manual pushing items around or fiddling with the array length.

How to make JS wait for previous lines of code to finish first?

So i have an issue with JS and nodeJS, and is that it runs whole code at the same time and doesnt wait for a previous function to finish its work (compared to python). How do i make it first finish its function, push the results to array and only then print to console the whole array? await doesnt seem to work in any kind of for loop
const fetch = require('node-fetch')
const fetchlink = async (i) => {
let url = `http://linktofetch`
let response = await fetch(url, {
method: 'GET'
})
const answer = await response.json()
return answer
}
const arr = []
let pushtoarr = async (value) => {
arr.push(value)
}
let main = async () => {
for(let i=1;i < 10; i++){
const answer = fetchlink(i).then((response) => {
response.data.items.forEach(el =>{
pushtoarr(el.name)
}
)
})
}
console.log(arr)
}
main()
When doing foo.then(bar), bardoesn't execute immediately, instead you're just registering a callback that will execute bar later on, and you should instead be doing const baz = await foo; bar(baz).
So, in your example, you should rewrite your code as:
const fetch = require('node-fetch')
const fetchlink = async (i) => {
let url = `http://linktofetch`;
let response = await fetch(url, { method: 'GET' });
const answer = await response.json();
return answer;
}
(async () => {
const arr = [];
for (let i=1; i<10; i++) {
const response = await fetchLink(i);
for (const el of response.data.items) {
arr.push(el.name);
}
}
console.log(arr);
})();
Didn't test but it will look somehow like this
const fetch = require('node-fetch')
(async () => {
const arr = []
for(let i=1;i < 10; i++) {
const response = await fetchlink(i)
const answer = response.data.items.forEach(el => arr.push(el))
}
console.log(arr)
})()
async function fetchlink (i) {
let url = `http://linktofetch`
let response = await fetch(url, {
method: 'GET'
})
return response.json()
}
The problem is that you're trying to do an asynchronous task synchronously. There are generally two ways you can go about executing an async function and which one you use depends on what you need from the function.
Non-Blocking
In general, an async function will return a Promise. In order to get the results of a promise you have to unwrap it like so,
asyncFunction(args).then((promiseResult) => { doStuff(promiseResult); });
The key part is that you unwrap the promise using then which will only trigger after the original promise has finished. This means that code execution will not wait for the promise to get unwrapped to execute the lines after. For example:
asyncFunction(args).then((promiseResult) => { doStuff(promiseResult); });
console.log('done');
In this case the log function will generally happen before the doStuff function gets called.
Blocking
In the event that you want to block or wait for a promise to unwrap, you need to use the await keyword like so,
const promiseResult = await asyncFunction(args);
doStuff(promiseResult);
console.log('done');
In this example, no code after the await line will get executed until the asyncFunction resolves. The important thing to understand is that it only is true within the scope of code you are in. If there is a non-blocking async function being executed inside of asyncFunction, it will not wait to finish resolving that before returning to doStuff.
I will omit the actual modification to fix your code as it seems a few other people have beat me to that, however, I hope that explanation helps.
Use promise
example usage below
'use strict';
var promiseCount = 0;
function testPromise() {
let thisPromiseCount = ++promiseCount;
let log = document.getElementById('log');
log.insertAdjacentHTML('beforeend', thisPromiseCount +
') Started (<small>Sync code started</small>)<br/>');
// We make a new promise: we promise a numeric count of this promise, starting from 1 (after waiting 3s)
let p1 = new Promise(
// The executor function is called with the ability to resolve or
// reject the promise
(resolve, reject) => {
log.insertAdjacentHTML('beforeend', thisPromiseCount +
') Promise started (<small>Async code started</small>)<br/>');
// This is only an example to create asynchronism
window.setTimeout(
function() {
// We fulfill the promise !
resolve(thisPromiseCount);
}, Math.random() * 2000 + 1000);
}
);
// We define what to do when the promise is resolved with the then() call,
// and what to do when the promise is rejected with the catch() call
p1.then(
// Log the fulfillment value
function(val) {
log.insertAdjacentHTML('beforeend', val +
') Promise fulfilled (<small>Async code terminated</small>)<br/>');
}).catch(
// Log the rejection reason
(reason) => {
console.log('Handle rejected promise ('+reason+') here.');
});
log.insertAdjacentHTML('beforeend', thisPromiseCount +
') Promise made (<small>Sync code terminated</small>)<br/>');
}
reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

asyncronous callbacks inside for loop

var a = ['url1', 'url2', 'url3'];
var op = [];
cb = (callback) => {
for (var i = 0; i < a.length; i++) {
gtts.savetos3(`${a[i]}.mp3`, a[i], 'ta', function(url) {
console.log(url['Location']);
op.push(url['Location']);
});
}
callback()
}
cb(() => {
console.log(op);
})
In the above code the gtts.savetos3 is an asynchronous function.It takes significance amount of time to complete the execution for every element in array.Due to asynchronous feature I cannot print the complete array of url in op array as it prints an empty array.
gtts.savetos3 function calls the given callback with correct url so that i can print the output using console.log but when comes to looping i got messed up.
My question is
How to make the callback function called only after the execution of
the all array elements get processed by the gtts.savetos3 function.
can we achieve the solution for above problem without Promise.all or without Promise with the help of using only callbacks.
Thanks in Advance ...!
You can keep a counter and increase it inside the methods's callback,
call your done callback only when the counter reaches the length of
the array.
cb = (done) => {
let counter = 0;
for (let i = 0; i < a.length; i++) {
gtts.savetos3(`${a[i]}.mp3`, a[i], 'ta', function (url) {
console.log(url['Location']);
op.push(url['Location']);
++counter;
if (counter == a.length) {
done();
}
});
}
}
cb(() => {
console.log(op);
})
This is just a way to solve the problem without Promises or any third party module but not the elegant or correct way.
If you want to stick to the callback and are ok to use third-party module have a look on
Async waterfall method.
If you are using aws-sdk's s3 put object, then sdk already provides a
promisified methods as well, you can simply append your method with
.promise to get the same.
To solve the problem with promises just change your wrapper into an
async function.
async savetos3(...parametres) {
//Some implementation
let res = await S3.putObject(....params).promise();
//Some implementation
}
cb = Promise.all(a.map(name => savetos3(`${name}.mp3`, name , 'ta')));
cb.then(() => {
console.log(op);
})
Here is my solution.
const a = ['url1', 'url2', 'url3'];
const op = [];
const saveToS3 = name => new Promise((resolve, reject) => {
gtts.savetos3(`${name}.mp3`, name, 'ta', function (url) {
console.log(url['Location']);
resolve(url)
});
})
Promise.all(a.map(item => saveToS3(item))).then(() => {
console.log(op)
})

Categories

Resources