Typescript - subscription inside iteration - arrays

I am trying to call the "getData" API over and over for each value in the "myArray" variable. Each time it gets new data from the "getData" call, I am trying to push the result to an array so I can manipulate the values in the "destinationArray" section of code. However, since typescript is not async, it gets to the "destinationArray" code before it's finished in the .subscribe / before it's finished iterating.
I've already tried fixing this with await waitUntil(() => done==true, { timeout: 10015 });
but I keep getting randomly appearing messages in the console saying ERROR Error: Uncaught (in promise): Error: Timed out after waiting for 10015 ms. The naive answer is to just increase the timeout to infinity, but the actual API call itself does not take 10 seconds, it takes about 1 second (if even that long). How can I make it wait until it's finished iterating / subscribing before it moves onto the "destionationArray" section at the bottom, without seeing that timeout error message in the console?
let dataFromAPIcall: any[] = []
let myArray: any[] = ["hello","world"]
for(let i = 0; i< myArray.length; i++) {
this.GetDataSubScription = this.myService.getData(myArray[i]).pipe( takeUntil(this.ngUnsubscribe) ).subscribe(data => {
dataFromAPIcall.push(data)
if(i+1 == myArray.length) {
done = true
}
});
}
await waitUntil(() => done==true, { timeout: 10015 });
let destinationArray: any[] = [];
for(let i = 0; i < dataFromAPIcall.length; i++) {
destinationArray[i] = [dataFromAPIcall[i].something1, dataFromAPIcall[i].something2]
}

You can zip your api calls and handle all responses in a reactive way, without using imperative state variables:
After all observables emit, emit values as an array
// creating an array of observables. The requests are not fired yet.
const requests = myArray.map(i => this.myService.getData(i));
const destinationArray: any[] = [];
// passing the request array to the zip function.
// this will actual initiate the requests and emit when ALL
// requests are done
zip(...requests).pipe(
tap(responses => {
// maping responses to destinationArray
destinationArray = responses.map(dataFromAPIcall => [dataFromAPIcall.something1, dataFromAPIcall.something2])
}),
tap(_ => {
// continue here
})
).subscribe();

Related

Discord.js message collector in a loop for tic tac toe

So i started to make a discord bot using discord.js which has a command for tic tac toe.
I tried using a message collector, like this in a while loop. After it enters the loop nothing happens. I added a console.log to see if the totalRouds variable is modifying and it was, so the loop somehow passed over the message collector code and executed just the last line.
while(numberOfrounds < 9){
const filter = m => m.author.id === message.author.id;
message.reply('Enter a position:');
message.channel.awaitMessages(filter, {max: 1, time: 3000, errors: ['time']}).then(collected => {
// code for drawing a canvas
}).catch(err => {
console.log(err);
});
console.log(totalRouds);
numberOfrounds ++;
}
.awaitMessages() returns a Promise that resolves into your collected message data asynchronously. That means that while your last log statement runs immediately after you start collecting messages, the collected messages are only available and thus processed at a later time.
To illustrate that, let me tidy your code a little and add some logs in the order that your code would normally run:
while (numberOfrounds < 9) {
console.log("1");
const filter = m => m.author.id === message.author.id;
message.reply('Enter a position:');
console.log("2");
message.channel.awaitMessages(
filter,
{ max: 1, time: 3000, errors: ['time'] }
).then(collected => {
console.log("5, after resolving, the collected data is only now avaiable");
}).catch(err => {
console.log(err);
});
console.log("3");
console.log(totalRouds);
numberOfrounds++;
console.log("4, immediately starts the next loop, back to #1 again");
}
I presume that your totalRouds variable is defined within the callback function passed into .then(). So other than the typo (let's fix that), your variable would be defined in the wrong scope and thus totalRounds would always remain undefined, even after the Promise resolves with your collected messages, you process those messages, you set totalRounds within the callback function etc. So here is our updated snippet:
while (numberOfrounds < 9) {
const filter = m => m.author.id === message.author.id;
message.reply('Enter a position:');
message.channel.awaitMessages(
filter,
{ max: 1, time: 3000, errors: ['time'] }
).then(collected => {
let totalRounds = 1 // Do your processing here
console.log(totalRounds); // This should log as expected
}).catch(err => {
console.log(err);
});
numberOfrounds++;
}
But this is still probably not the behaviour you are looking for. Why? Your bot will attempt to send all 9 replies (assuming your numberOfRounds starts at 0 earlier) at once, at which point Discord.js will automatically send them in batches to avoid spamming the API, and all message collectors would be waiting concurrently. You probably intend to "pause" or suspend processing until the Promise returned by .awaitMessages() resolves and you have finished processing the returned data, causing synchronous behaviour whilst using an asynchronous method call (therefore you say "the loop somehow passed over the message collector code and executed just the last line"). To do this, we can use async-await:
/*
You have not provided your full code,
so what you need to do is mark your message event's
callback function as async.
Refer to the linked article on MDN.
*/
while (numberOfrounds < 9) {
const filter = m => m.author.id === message.author.id;
message.reply('Enter a position:');
/*
Then, wait for the Promise returned by
this promise chain to resolve
before resuming operation
and moving on to the next iteration.
*/
await message.channel.awaitMessages(
filter,
{ max: 1, time: 3000, errors: ['time'] }
).then(collected => {
let totalRounds = 1
console.log(totalRounds);
}).catch(err => {
console.log(err);
});
numberOfrounds++;
}
My terminology may not be 100% correct, but this is my understanding. Feel free to comment if improvements can be made.

ReactJS : update state regularly in a specific time interval

I am creating a sensor app, where I have to update the state regular in a time interval of 15 seconds with the data receiving from the backend. Eventhough I can get the data successfully from the backend in the time interval, I cannot update the state in ReactJS. The code is as follows:
async regularCallAPI(){
let result;
const url = "http://..../...";
await fetch(url).then( res => res.json()).then( res => { result = res});
console.log("Regular Data from Backend : ", result);
let updatedSensors = result.map( sensor => {
return {
floorNum: sensor.floor_no,
roomNum: sensor.room_no,
sensorStatus: sensor.sensor_status,
alertStatus: sensor.alert_status,
ownerName: sensor.owner_name,
email: sensor.email,
phoneNumber: sensor.phone_number,
recordedDateAndTime: new Date(sensor.created_date).toLocaleString()
}
});
await this.setState({ sensors: updatedSensors });
console.log("sensor state updated ...");
}
and I have executed the above function in a interval of 15 seconds ;
timerId = setInterval(this.regularCallAPI, 15000);
The problem I have is, it runs the first time successfully and then when it run the second time, I get this error:
Unhandled Rejection (TypeError): this.setState is not a function
and yes, the function is already binded to the component in the constructor. What is the reason for this behavior and what is the possible solution for this ?
The callback is made in a different context. You need to bind to this in order to have access inside the callback:
setInterval( () => this.regularCallAPI(), 15000);

async inside a for loop nodejs

I am trying to access an api and I will have to run the api calls several times based on the page numbers I need to iterate, the following is the code which I am using and how can I get the all the response pushed into an array.
as nodeJs is single threaded It is not waiting for the responses from the api.
How can I can tackle this and ensure all the response values are being pushed into an array
Inside the for loop I want the final array which has all the values of the api response. So, I check the total page value and response page Number if that matches which means that will be the last page and I push the array to another function but when I do that it does not have all the values because nodejs does not wait for the api response.
const fs = require('fs');
var pepKey = 'asdfasdfasd';
var pepResponse;
var pepTimecards = [];
pep();
function pep(){
var options = {
headers: {
"content-type": "application/json",
},
agentOptions: {
pfx: fs.readFileSync('./certificate/asdfsdaf.p12'),
passphrase: 'asdasdsda'
}
};
request.get('https://source.asdfasdf.io/api/organisations/asdfasdf/timecard_keys?timecard_type=Flex',options, (err, res, body) => {
if (err) { return console.log(err); }
pepResponse = JSON.parse(body)
pepTimecards = pepResponse.data;
if(pepResponse.pages > 1){
for(let i=2;i<=pepResponse.pages;i++){
var url = 'https://source.`pepme`.io/api/organisations/sdfsadf/timecard_keys?timecard_type=Flex&page='+pageNo;
request.get(url,options, (err, res, body) => {
if (err) { return console.log(err); }
body = JSON.parse(body)
pepTimecards = pepTimecards.concat(body.data)
if(pepResponse.pages == body.page){
console.log(pepResponse.pages)
console.log(body.page +"body page")
console.log(pepTimecards)
}
});
}
}else{
}
});
}
Use the request-promise library which supplies promisified versions of the request library. Then, you can use async/await in your for loop to serialize your operations:
Newer answer to go with the edited code in the OP's question
const fs = require('fs');
const rp = require('request-promise');
const pepKey = 'asdfasdfasd';
pep().then(pepTimecards => {
// the timecard data is valid in here
console.log(pepTimecards);
}).catch(err => {
console.log(err);
});
async function pep() {
let timecards = [];
const options = {
headers: {
"content-type": "application/json",
},
agentOptions: {
pfx: fs.readFileSync('./certificate/asdfsdaf.p12'),
passphrase: 'asdasdsda'
},
json: true,
uri: 'https://source.asdfasdf.io/api/organisations/asdfasdf/timecard_keys?timecard_type=Flex'
};
let pepResponse = await rp(options);
timecards = pepResponse.data;
if (pepResponse.pages > 1) {
for (let i = 2; i <= pepResponse.pages; i++) {
options.uri = 'https://source.`pepme`.io/api/organisations/sdfsadf/timecard_keys?timecard_type=Flex&page='+pageNo;
let body = await rp(url, options);
// add body.data onto the existing array
timecards.push(...body.data);
}
} else {
}
console.log(pepResponse.pages)
console.log(timecards)
return timecards;
}
Prior Answer before OP edited the code in their question:
const rp = require('request-promise');
// I'm assuming this is some sort of method definition on a class, otherwise it needs the function keyword
async pageno(pageNo) {
for (let i=2;i<=pepResponse.pages;i++){
try {
options.uri = 'https://test/timecard_keys?timecard_type=asdas&page='+pageNo;
// let request-promise parse the json for you automatically
options.json = true;
let body = await rp(options);
pepTimecards = pepTimecards.concat(body.data)
if (pepResponse.pages == body.page){
console.log(pepResponse.pages)
console.log(body.page +"body page")
console.log(pepTimecards)
}
} catch(e) {
// decide what to do for error handling
// this will log and rethrow so the caller will get a rejected promise
console.log(e);
throw e;
}
}
// return some value here to be the resolved value of the returned promise
return pepTimecards;
}
In your code, it is not clear where the options, pepTimecards, pepResponse variables are declared. They should probably be declared as local variables here or passed in to the function and/or returned from your function.
Summary of modifications:
Add async to method declaration so we can use await.
Load request-promise library into rp variable
Add options.json = true to the let the request-promise library parse the JSON result for us automatically
Change rp() to just use the options structure (add URL to that)
Add try/catch to catch any errors from the await, log them, then rethrow so pageno() will return a promise that rejects if there is an error (you can customize the behavior when there's an error if desired)
Add a return value so there is meaningful resolved value to the promise (you should not be using side-effect programming as it is now (modifying variables that are not passed in, declared locally or returned).
Things for you still to fix:
Stop using side-effect programming where you modify free variables that aren't passed in, aren't declared locally and aren't returned. This is a bad way to design code. You don't show enough overall context from the calling code or where these other variables are defined to make a concrete recommendation on how it should be done.
Decide what your error handling strategy is if there's an error on one of the requests and implement that strategy and proper handling.

Troubles with Promises

I'm doing an Ionic project and I'm getting a little bit frustrated whit promises and '.then()' although I've read a lot of documentation everywhere.
The case is that I have one provider with the functions loadClients and getWaybills.
The first one gets all the clients that have waybills and the second one gets all the waybills from one concrete client.
loadClients() {
return new Promise(resolve => {
this.http.get('http://localhost/waybills?fields=descr1_sped&idUser='+ this.id)
.map(res => res)
.subscribe(data => {
this.data = data.json();
resolve(this.data);
});
});
}
// GET WAYBILLS
getWaybills(client) {
return new Promise(resolve => {
this.http.get('http://localhost/waybills/?stato=0&idUser='+ this.id +'&descr1_sped='+ client)
.map(res => res)
.subscribe(data => {
this.data = data.json();
resolve(this.data);
});
});
}
On the other hand, on the component welcome.ts I have a function loadWaybills which is called on the view load and is executing the following code, my idea is to get all the clients and then get the respective waybills of each one. Then I'll take just of the ones that are defined.
The problem is that on the second .then() instead of getting the variable data I'm getting just undefined... I've understood that if you put a synchronous code inside .then() can be properly executed and work with the "data" which is the result of the promise. Why am I getting this undefined?
loadWaybills() {
//We first load the clients
this.waybills.loadClients()
.then(data => {
this.waybill = data;
var preClients = this.waybill;
this.clients = [];
//Here we're deleting duplicated clients and getWaybills of them)
for (let i = 0; i < preClients.length; i++) {
if (this.clients.indexOf(preClients[i].descr1_sped) == -1) {
this.waybills.getWaybills(preClients[i].descr1_sped)
.then(data => {
**//Here we'll check if the clients has waybills or not**
this.clientWaybills[i] = data;
this.clients.push(preClients[i].descr1_sped)
});
}
}
});
}
It is hard to say because we don't know what the API is meant to return. For example, there may be a missing field somewhere from the first GET and now for the second one, it returns as undefined sometimes. If it only returns undefined sometimes, a simple solution to this, would be to check that the value is defined before assigning it to the variable.
If it always returns as undefined and shouldn't, try to debug the code and make sure that the values are present before the second .then.

Can Looping Through a Function "Too Fast" Break It (Node.js)

I am trying to accumulate API responses on a server and return them to the client as a single object. To do this I am looping through items in an array and mapping the responses back into the original object. This is working fine for an array of length 1, but logs blanks when looping through larger arrays.
When looping through the array does Node create a new instance of the function or does it keep passing data into the same function even if it hasn't returned a value yet?
loopThroughArray(req, res) {
for(let i=0; i<req.map.length; i++) {
stack[i] = (callback) => {
let data = getApi(req, res, req.map[i], callback)
}
}
async.parallel(stack, (result) => {
res.json(result)
})
}
....
function getApi(req, res, num, cb) {
request({
url: 'https://example.com/api/' + num
},
(error, response, body) => {
if(error) {
// Log error
} else {
let i = {
name: JSON.parse(body)['name'],
age: '100'
}
console.log(body) // Returns empty value array.length > 1 (req.map[i])
cb(i)
}
})
If Node is overloading the function, how can I ensure data has been received before running the function again?
The api calls are async.
When running the loop, the code is making many rest calls without waiting for the answer.
If the loop is not too big then you could synchronize the calls using recursion.
You could also synchronize the calls using nimble:
http://caolan.github.io/nimble/
a loop waits for the method inside it to finish before it loop back. so there's really no such thing as a loop that moves really fast unless you're using "threading" which your obviously not.

Resources