Hi I am pretty new to Angular and Observables
I am trying to GET Objects by theirs ID through a loop.
But don't receive my Response in Order.
Example
get ID(1)
get ID(2)
get ID(3)
Receive Object ID(2)
Receive Object ID(3)
Receive Object ID(1)
Is it possible to get my Objects back in order ??
Below is where I call multiple times my service function :
conferences-attendance.component.ts
ExportExcelAttendance() {
for (var i = 0; i < this.contactsAttendance.length; i++) {
this.practiceService.GetPracticebyDBID(this.contactsAttendance[i].practiceId)
.subscribe(
(practice: Practice) => {
this.practicesAttendance.push(practice);
if (this.practicesAttendance.length == this.contactsAttendance.length) {
this.ExportExcelAttendance2();
}
},
error => this.errorMessage = <any>error
);
}
}
Here is my function in my service, it where I receive the data (not in order with the calls).
practices.service.ts
GetPracticebyDBID(id: string) {
let params: URLSearchParams = new URLSearchParams();
params.set('thisId', id);
let requestOptions = new RequestOptions();
requestOptions.params = params;
return this.http.get('http://ec2-34-231-196-71.compute-1.amazonaws.com/getpractice', requestOptions)
.map((response: Response) => {
return response.json().obj;
})
.catch((error: Response) => Observable.throw(error.json()));
}
forkJoin gives you a little less code,
const arrayOfFetches = this.contactsAttendance
.map(attendee => this.practiceService.GetPracticebyDBID(attendee.practiceId) );
Observable.forkJoin(...arrayOfFetches)
.subscribe((practices: Practice[]) => {
this.practicesAttendance = practices;
this.ExportExcelAttendance2();
});
Edit
Snap! #Anas beat me to it. Although, I don't think you need the concatAll()
you should use concatAll operator to ensure calling your observables in sequence.
also, you can use completed callback to call ExportExcelAttendance2 instead of checking practicesAttendance length on every response callback.
check the below example:
let contactsAttendanceObservables = this.contactsAttendance
.map((item) => {
return this.practiceService.GetPracticebyDBID(item.practiceId);
});
Observable.of(...contactsAttendanceObservables)
.concatAll()
.subscribe(
(practice: Practice) => {
this.practicesAttendance.push(practice);
},
(err) => {
// handle any errors.
},
() => {
// completed
this.ExportExcelAttendance2();
}
);
if you still want your observables to run in parallel, you can use forkJoin Operator, which will emit the last value of all the passed observables to a one subscriber when all observables are completed.
check the below example:
let contactsAttendanceObservables = this.contactsAttendance
.map((item) => {
return this.practiceService.GetPracticebyDBID(item.practiceId);
});
Observable.forkJoin(...contactsAttendanceObservables)
.subscribe(
(practices: Practice[]) => {
this.practicesAttendance = practices;
this.ExportExcelAttendance2();
}
);
The forkJoin operator is simple to use. It waits until all observables complete, then emit an array with all the items emitted.
ExportExcelAttendance() {
const all = this.contactsAttendance.map(it => this.practiceService.GetPracticebyDBID(it.practiceId));
Rx.Observable.forkJoin(all)
.subscribe(
practicesAttendance => this.ExportExcelAttendance2(practicesAttendance),
error => this.errorMessage = < any > error);
}
Related
I am developing a server project which needs to call some functions synchronously. Currently I am calling it in asynchronous nature. I found some similar questions on StackOverflow and I can't understand how to apply those solutions to my code. Yet I tried using async/await and ended up with an error The 'await' operator can only be used in an 'async' function
Here is my implementation
function findSuitableRoom(_lecturer, _sessionDay, _sessionTime, _sessionDuration, _sessionType){
let assignedRoom = selectRoomByLevel(preferredRooms, lecturer.level, _sessionDuration); <------- Need to be call synchronously
if (!assignedRoom.success){
let rooms = getRooms(_sessionType); <------- Need to be call synchronously
assignedRoom = assignRoom(_lecturer.rooms, _sessionDuration, _lecturer.level);
} else {
arr_RemovedSessions.push(assignedRoom.removed)
}
return assignedRoom;
}
function getRooms(type){
switch (type){
case 'Tutorial' : type = 'Lecture hall'
break;
case 'Practical' : type = 'Lab'
break;
default : type = 'Lecture hall'
}
Rooms.find({type : type},
(err, rooms) => {
if (!err){
console.log('retrieved rooms ' + rooms)
return rooms;
}
})
}
Here I have provided only two methods because full implementation is very long and I feel if I could understand how to apply synchronous way to one method, I can manage the rest of the methods. Can someone please help me?
Well yes await is only available inside an async function so put async infront of findSuitableRoom.
Also you did a classic mistake. You use return inside of a callback function, and expect getRooms to return you some value.
async function findSuitableRoom(
_lecturer,
_sessionDay,
_sessionTime,
_sessionDuration,
_sessionType
) {
let assignedRoom = selectRoomByLevel(
preferredRooms,
lecturer.level,
_sessionDuration
);
if (!assignedRoom.success) {
try {
let rooms = await getRooms(_sessionType);
} catch (err) {
console.log("no rooms found");
}
assignedRoom = assignRoom(
_lecturer.rooms,
_sessionDuration,
_lecturer.level
);
} else {
arr_RemovedSessions.push(assignedRoom.removed);
}
return assignedRoom;
}
Also wrap it in an try / catch
Since .find() returns an promise if you dont pass an callback you can write it like this
function getRooms(type) {
switch (type) {
case "Tutorial":
type = "Lecture hall";
break;
case "Practical":
type = "Lab";
break;
default:
type = "Lecture hall";
}
return Rooms.find({ type });
}
Note here findSuitableRoom is no longer synchronouse. Its async and returns an promise. That means you will need to use the function like this:
findSuitableRoom.then(res => { console.log(res); })
The 'await' operator can only be used in an 'async' function
This means whenever you want to use the await keyword it needs to be inside a function which has an async keyword (returns promise)
read this https://javascript.info/async-await for more info
const add = (a, b) => {
return new Promise((resolve, reject) => {
setTimeout(() => { //to make it asynchronous
if (a < 0 || b < 0) {
return reject("don't need negative");
}
resolve(a + b);
}, 2000);
});
};
const jatin = async () => {
try{
const sum = await add(10, 5);
const sum2 = await add(sum, -100);
const sum3 = await add(sum2, 1000);
return sum3;
} catch (e) {
console.log(e);
}
};
jatin()
Try this
let's understand this
add is a normal function that does some asynchronous action like
waiting for 2 seconds
normally we use await with async function so in order to use it we make as async function jatin and use await with add function call
to make it synchronous, so until first await add call() doesn't
happen it wont execute another await add call().
Example code if you will use in your app.js
router.post("/users/login", async (req, res) => {
try {
const user = await User.findByCredentials(
req.body.email,
req.body.password
);
const token = await user.generateToken();
res.status(200).send({
user,
token,
});
}
catch (error) {
console.log(error);
res.status(400).send();
}
});
i have array of db like
const dbArr = ["http://localhost:5984", "http://xyz_couchdb.com:5984"]
data to insert
let data ={
_id: 324567,
name: Harry,
gerder: male
}
here is the logic i am using nano module
return new Promise((resolve, reject) => {
let res = [];
let rej = [];
let counter = 0;
for(let i = 0; i < dbArr.length ; i++){
dbArr[i].insert(data, (err, body) => {
err ? rej.push(err) : res.push(body)
if(counter === obj.dbArray.length -1){
rej.length ? reject(rej) : resolve(res)
}
counter++;
})
}
})
what can be the best possible way to achieve this using promise or async module or anything.
In the following example, we gotta use Array.map to create one promise for each element of dbArr, then we gotta wait all promises to end using Promise.all. The catch is here so we handle the errors.
function getAll(dbArr) {
return Promise.all(dbArr.map(x => x.insert(data)));
}
getAll(dbArr)
.then((rets) => {
// Handle the returns
// They are in an array
})
.catch((err) => {
// Handle the error
});
EDIT :
Ok after checking out the documentation of node-couchdb (the one I suppose you use) - I saw that the .insert() method do not return a Promise but only a callback.
So we gotta transform the method, so it will return a Promise using util.Promisify()
const {
promisify,
} = require('util');
function getAll(dbArr) {
return Promise.all(dbArr.map(x => promisify(x.insert)(data)));
}
getAll(dbArr)
.then((rets) => {
// Handle the returns
// They are in an array
})
.catch((err) => {
// Handle the error
});
My application uses an internal webservice for fetching data, i have a job which creates approx 500 requests which getsfired async to complete the fetch operation.
I make use of Axios, by creating an array of axios promises and then resolving them using using Axios.all();
It works fine until some 200 requests but post that i get socket hung up, however on the server side i see the requests are being processed.
How to configure axios to set custom time out, or is it a better idea to splice my promises array and then run them as multiple batches ?
Source code
let getAxiosPromiseArray = (urlList) => {
var axiosArrayofPromise = [];
return new Promise ( (resolve, reject) => {
try {
urlList.forEach ( (URL) => {
axiosArrayofPromise.push(axios.get(URL));
});
resolve(axiosArrayofPromise);
}
catch (err) {
reject("There is a problem getting Axios array of promises " + err);
}
})
}
async function processAxiosPromises (PromiseArray) {
try {
var results = []
results = await axios.all(PromiseArray);
return results;
}
catch(err) {
throw("There was a problem resolving promises array (Axios) " + err);
}
}
getallID().then ( (urlList) => {
return getAxiosPromiseArray(urlList);
}).then( (AxiosPromises) => {
return processAxiosPromises(AxiosPromises);
}).then ((resultData) => {
console.log(resultData);
});
Error
There was a problem resolving promises array (Axios) Error: socket hang up
First, that pair of functions getAxiosPromiseArray() and processAxiosPromises() needs fixing.
Your new Promise() construction is unnecessary. You can simply return Promise.all(arrayofPromise) (or axios.all(...) if you must) and do away with the other function.
Renaming the remaining function to something meaningful, you would end up with eg :
let getData = (urlList) => {
return Promise.all(urlList.map(URL => axios.get(URL)))
.catch(error => {
error.message = "There is a problem getting Axios array of promises " + error.message; // augment the error message ...
throw error; // ... and re-throw the errror.
});
};
And call as follows :
getallID().then(getData)
.then(resultData => {
console.log(resultData);
}).catch(error => {
console.error(error);
});
That will put you on solid ground but, on its own, is unlikely to fix a concurrency problem (if that's what it is), for which the simplest approach is to use Bluebird's Promise.map with the concurrency option.
The caller code can remain the same, just change getData(), as follows:
let getData = (urlList) => {
let concurrency = 10; // play with this value to find a reliable concurrency limit
return Promise.map(urlList, URL => axios.get(URL), {'concurrency': concurrency})
.catch(error => {
error.message = "There is a problem getting Axios array of promises " + error.message;
throw error;
});
};
// where `Promise` is Bluebird.
const axios = require('axios');
const axiosThrottle = require('axios-throttle');
//pass axios object and value of the delay between requests in ms
axiosThrottle.init(axios,200)
const options = {
method: 'GET',
};
const urlList = [
'https://jsonplaceholder.typicode.com/todos/1',
'https://jsonplaceholder.typicode.com/todos/2',
'https://jsonplaceholder.typicode.com/todos/3',
'https://jsonplaceholder.typicode.com/todos/4',
'https://jsonplaceholder.typicode.com/todos/5',
'https://jsonplaceholder.typicode.com/todos/6',
'https://jsonplaceholder.typicode.com/todos/7',
'https://jsonplaceholder.typicode.com/todos/8',
'https://jsonplaceholder.typicode.com/todos/9',
'https://jsonplaceholder.typicode.com/todos/10'
];
const promises = [];
const responseInterceptor = response => {
console.log(response.data);
return response;
};
//add interceptor to work with each response seperately when it is resolved
axios.interceptors.response.use(responseInterceptor, error => {
return Promise.reject(error);
});
for (let index = 0; index < urlList.length; index++) {
options.url = urlList[index];
promises.push(axiosThrottle.getRequestPromise(options, index));
}
//run when all promises are resolved
axios.all(promises).then(responses => {
console.log(responses.length);
});
https://github.com/arekgotfryd/axios-throttle
I have a Promise chain that runs like this:
// this part is not meant to be syntactically correct
axios.get(<rest_api_that_queries_a_list_of_car_models>).then(res => {
// loop thru list and call a custom module promise
for (...) {
mymodule.getSomething(args).then(res => {
axios.post(<rest_write_to_db>).then(res => {
//we're done
....
// in mymodule
function getSomething(args) {
return getAnotherThing(args).then(res => {
// do stuff
return aThing
...
function getAnotherThing(args) {
return getThatThing(args).then(res => {
// see if pagination is greater than 1 page
if (pages == 1)
return res
let promises = [res]
for (x=2;x<pages;x++) {
// change args
promises.push( getThatThing(args))
}
return Promise.all(promises)
}).then(allres => {
return allres
})
...
// this is where it's breaking. this part is syntactically accurate
function getThatThing(args) {
let params = Object.assign(BASE_PARAMS, args.params)
console.log(args.params.model) // this logs prints a different model everytime
return axios.get(URL, {
headers: {
"Accept": ACCEPT,
"Content-Type":CONTENT_TYPE,
},
params: params
}).then (response => {
console.log(response.request.path) // this path includes the last key only everytime. so if there are 10 car models, this will search for the last model 10 times.
let result = response.data
return result
}).catch(function (error) {
console.log("search error:",error);
return error.response.data.errorMessage[0].error[0].message[0]
})
}
So basically the issue is that the axios.get command in the last function is using the same get parameters even tho I'm printing different parameters right before I make the call. I don't see how that is possible.
I was able to fix the issue by changing this line
let params = Object.assign(BASE_PARAMS, args.params)
to this
let params = {...BASE_PARAMS, ...args.params}
I can't really tell you why this fixed it. I'm assuming the Object.assign set the value to params on a global level. Perhaps someone else could provide more insight.
I use the request-promise node module as following :
let rp = require('request-promise');
I have the following promises chain :
getData().then( data => {
return getUser(data);
})
.then( data => {
return getProfiles(data);
}).then( data => {
updateAddresses(data);
});
The updateAddresses is as following :
function updateAddresses(data){
var promises = data.map( (aProfile) => {
var options = {url:'http://example.com', method:'POST'};
return rp(options);
});
// Promise.all(promises).then(...);
}
So I'm preparing a promise (request-promise) for each element of the array.
The problem is that those promises are firing even when I remove Promise.all !
How is that possible ? How can I make the promise not firing ?
Regards.
Your updateAddresses is not returning anything to call then() on and you aren't waiting on what would be coming out of updateAddress.
function updateAddresses(data){
var promises = data.map( (aProfile) => {
var options = {url:'http://example.com', method:'POST'};
return rp(options);
});
//Return something to then()
return Promise.all(promises).then(...);
}
Then wait on it:
getData().then( data => {
return getUser(data);
})
.then( data => {
return getProfiles(data);
}).then( data => {
return updateAddresses(data);
}).then(e => somethingelse);
Per your requirement to store the mapped address Objects:
function updateAddresses(data) {
return data.map((aProfile) => {
return {
url: 'http://example.com',
method: 'POST'
}
});
}
getData()
.then(data => getUser(data))
.then(data => getProfiles(data))
.then((data) => {
let addressOptions = updateAddresses(data);
/*
* You now have an Array of Objects of the form:
* {
* url: "",
* method: "",
* }
*/
return Promise.resolve(addressOptions);
})
.then(addressOptions => Promise.all(addressOptions.map(address => rp(address))))
.then((arrayOfResolvedData) => {
/*
* arrayOfResolvedData is an Array of data per whatever rp() returns
* probably Response Objects
*/
console.log(arrayOfResolvedData);
})
You could do what zero298 suggested in his edit.
the main problem is that you map your data to an actual promise, rather than something that is ready to execute a promise,
compare
Let promises = Options.map(option => execute(option))
Promise.all(promises)
With
Let promiseGenerators =Options.map(option => () => execute(option))
Promise.all(promiseGenerators.map(generator => generator())
The second you call the function that will return the promise, it will start doing it. That's where the problem was, because you called rp(options) too soon.