I'm performing multiple task and each task is dependent on previous task execution. So in my example what I want is after getting all the Id, i should get their respective blob value and then finish the execution by storing it in a variable. I'm very new to javascript and angular, please help me out. Here's what I'm trying
//this method will get the response from the rest api
async getIDFromAssets(){
this.blobDataArray=[];
this.service.getAssetsData().subscribe(async (res: JSON) => {
//after getting the response I'm filtering through it to get sepcific Id using this.getFileId() method
this.getFileId(res).then((data)=>{
console.log("blob "+data)
})
})
}
//below method will get one Id at a time and will call another method to get it's blob value
async getFileId(res){
this.fileId = [];
Object.keys(res).forEach(keys => {
if (keys == 'emb') {
let responseValue = res[keys];
Object.keys(responseValue).forEach(async (keys1) => {
if (keys1 === 'file') {
let responseArray = responseValue[keys1];
for (let file of responseArray) {
let temp: string = file.metadata.contentType;
if (temp.startsWith('image')) {
//Here I'm getting id value 'file._id' and using that I'm calling another method 'getBlobData()' to get its blob value
let data=await this.getBlobData(file._id);
this.blobDataArray.push(data);
}
}
return this.blobDataArray
}
});
}
});
}
// method to get the blob value
async getBlobData(fileId){
this.articleDetailService.getBlobDataFromAssets(fileId).subscribe(async (res)=>{
let imageObj={
'id':fileId,
'blob':res
}
return imageObj;
})
}
You need to use RxJs to avoid the nested subscription to chain your calls, possible methods to use are mergeMap and filter
Please take a look at this answer here.
Related
I have the following problem: I want to get data from a specific node from firebase during runtime. It should display "stats" of a player that was selected before. Now I could use on() to get all the data in the beginning, but I want to save data transfers by only downloading the data of on player if I need to, so I tried to use once like this:
var firebaseRef = firebase.database().ref();
function getScoresOfPlayer(player) {
console.log(player);
var selectedPlayerScores = [];
firebaseRef.once('value').then(function(snap) {
snap.child('scores').child('thierschi').forEach(function(child) {
selectedPlayerScores.push([child.key, child.val()]);
});
});
return selectedPlayerScores;
}
The problem is that it retruns the array before the data was loaded into it. Also I checked the docs and didn't find a better solution.
Thanks in advance!
This is because the getScoresOfPlayer function returns selectedPlayerScores before the promise returned by the once() method resolves.
You should include the return within the then(), as follows:
var firebaseRef = firebase.database().ref();
function getScoresOfPlayer(player) {
console.log(player);
var selectedPlayerScores = [];
return firebaseRef.once('value') //return here as well
.then(function(snap) {
snap.child('scores').child(player).forEach(function(child) { //I guess it should be child(player) and not child('thierschi') here
selectedPlayerScores.push([child.key, child.val()]);
});
return selectedPlayerScores;
});
}
which means that you have to call your function as follows, since it is going to be asynchronous and to return a promise:
getScoresOfPlayer('xyz')
.then(function(selectedPlayerScores) {
....
})
What I have been trying to do is hit an endpoint for my blog posts and then with this data remove extra layout markup that came in from Wordpress. I am using Axios to make the request and then transform response option in order to modify the data to remove the extra markup from the "post_body" object inside my response. This works on a single blog post but when I try to do this all my blog blog posts it return an object of 20 or so blog posts. What I want to do is loop through the objects transform my data and then make a post request back to another API to publish my blog post. What I can't figure out if this will be possible once my promise is resolved. Would I be able to create another for loop within the .then and find my "post_body" object and make the post request. Not sure if I am thinking about this in the right way or not. Any help is much appreciated.
var fieldName = "et_pb";
var regExp = new RegExp("\\[\/?(" + fieldName + ".*?)\\]", "g");
function getBlogPosts() {
return axios.get(allPosts, {
transformResponse: axios.defaults.transformResponse.concat(function(data, headers) {
// use data I passed into the function and the objects from the API
// pass in data into the function using forEach this will return an array
data.objects.forEach(function(i) {
// use the returned array on Objects.key to find the name of the array
Object.keys(i).forEach(function(k) {
// if the key equals execute code
// console.log(k);
if (k === "post_body") {
// fire Regex
data[k] = i[k].replace(regExp, '');
// console.log(data[k])
}
})
})
return data;
})
})
}
axios.all([getBlogPosts()])
.then(axios.spread(function(blogResponse) {
console.log(blogResponse.data);
}));
#James you are correct . you can chain multiple requests as below or you can go for asyn and await options .
axios.get(...) // for allPosts
.then((response) => {
return axios.get(...); // using response.data
})
.then((response) => {
console.log('Response', response);
});
I am using angualr 4.
i have an array.
Here is my array;
this.bookings = [
{
'id':'dsjdsfhkdsjhfjkds01'
},
{
'id':'dsjdsfhkdsjhfjkds01'
}
]
I need retrieve data from database based on id.
Here is my script.
let scope = this;
scope.bookings.forEach(function(BookingItem){
var bId = BookingItem.id;
console.log("BId",bId);
scope.Bservice.getbooking(scope.at,bId).subscribe(booking => {
var responseVal = booking;
})
})
I need like forEach Take on firstvalue then get retrive data from database.After going to second value of booking then get data from database.
But i consoled value of bId.
ForEach taken on id values one by one After retreive data from database.
How can i fix this pblm.
Kindly advice me,
Thanks.
I am not understading you code at full but you have to do like this
You have to loop throught bookings array and than in argument of foreach you need vriable name not name of you class ,
Another thing is if you go in loop, you last returned value from ajax request will override vlaue of your variable , so better to store response in array thats why below code use array to store you response for each id.
let responseFromServer = new Array<any>();
this.bookings.forEach((bookingItem) => {
var bId=bookingItem.id;
console.log("BId",bId);
scope.Bservice.getbooking(scope.at,bId).subscribe(booking=>{
let response = new ResponseFromServer();
responseFromServer.Add(booking);
});
});
Since the api call response takes some time, you should wait for the response untill it is recieved.
You can use Observable forkJoin which works like a promise and processes all requests in loop and returns response.
import {Observable} from 'rxjs/Rx';
let observables = new Array();
this.bookings.forEach(function(booking){
observables.push(this.Bservice.getbooking(this.at,booking.id));
})
Observable.forkJoin(observables).subscribe(
res => console.log(res),
error => console.log('Error: ', error)
);
Here is a documentation for forkJoin
Specifically, given a list of data, I want to loop over that list and do a fetch for each element of that data before I combine it all afterward. The thing is, as written, the code iterates through the entire list immediately, starting all the operations at once. Then, even though the fetch operations are still running, the then call I have after all that runs, before the data could've been processed.
I read something about putting all the Promises in an array, then passing that array to a Promise.all() call, followed by a then that will have access to all that processed data as intended, but I'm not sure how exactly to go about doing it in this case, since I have nested Promises in this for loop.
for(var i in repoData) {
var repoName = repoData[i].name;
var repoUrl = repoData[i].url;
(function(name, url) {
Promise.all([fetch(`https://api.github.com/repos/${username}/${repoData[i].name}/commits`),
fetch(`https://api.github.com/repos/${username}/${repoData[i].name}/pulls`)])
.then(function(results) {
Promise.all([results[0].json(), results[1].json()])
.then(function(json) {
//console.log(json[0]);
var commits = json[0];
var pulls = json[1];
var repo = {};
repo.name = name;
repo.url = url;
repo.commitCount = commits.length;
repo.pullRequestCount = pulls.length;
console.log(repo);
user.repositories.push(repo);
});
});
})(repoName, repoUrl);
}
}).then(function() {
var payload = new Object();
payload.user = user;
//console.log(payload);
//console.log(repoData[0]);
res.send(payload);
});
Generally when you need to run asynchronous operations for all of the items in an array, the answer is to use Promise.all(arr.map(...)) and this case appears to be no exception.
Also remember that you need to return values in your then callbacks in order to pass values on to the next then (or to the Promise.all aggregating everything).
When faced with a complex situation, it helps to break it down into smaller pieces. In this case, you can isolate the code to query data for a single repo into its own function. Once you've done that, the code to query data for all of them boils down to:
Promise.all(repoData.map(function (repoItem) {
return getDataForRepo(username, repoItem);
}))
Please try the following:
// function to query details for a single repo
function getDataForRepo(username, repoInfo) {
return Promise
.all([
fetch(`https://api.github.com/repos/${username}/${repoInfo.name}/commits`),
fetch(`https://api.github.com/repos/${username}/${repoInfo.name}/pulls`)
])
.then(function (results) {
return Promise.all([results[0].json(), results[1].json()])
})
.then(function (json) {
var commits = json[0];
var pulls = json[1];
var repo = {
name: repoInfo.name,
url: repoInfo.url,
commitCount: commits.length,
pullRequestCount: pulls.length
};
console.log(repo);
return repo;
});
}
Promise.all(repoData.map(function (repoItem) {
return getDataForRepo(username, repoItem);
})).then(function (retrievedRepoData) {
console.log(retrievedRepoData);
var payload = new Object();
payload.user = user;
//console.log(payload);
//console.log(repoData[0]);
res.send(payload);
});
I'm using this Gumroad-API npm package in order to fetch data from an external service (Gumroad). Unfortunately, it seems to use a .then() construct which can get a little unwieldy as you will find out below:
This is my meteor method:
Meteor.methods({
fetchGumroadData: () => {
const Gumroad = Meteor.npmRequire('gumroad-api');
let gumroad = new Gumroad({ token: Meteor.settings.gumroadAccessKey });
let before = "2099-12-04";
let after = "2014-12-04";
let page = 1;
let sales = [];
// Recursively defined to continue fetching the next page if it exists
let doThisAfterResponse = (response) => {
sales.push(response.sales);
if (response.next_page_url) {
page = page + 1;
gumroad.listSales(after, before, page).then(doThisAfterResponse);
} else {
let finalArray = R.unnest(sales);
console.log('result array length: ' + finalArray.length);
Meteor.call('insertSales', finalArray);
console.log('FINISHED');
}
}
gumroad.listSales(after, before, page).then(doThisAfterResponse); // run
}
});
Since the NPM package exposes the Gumorad API using something like this:
gumroad.listSales(after, before, page).then(callback)
I decided to do it recursively in order to grab all pages of data.
Let me try to re-cap what is happening here:
The journey starts on the last line of the code shown above.
The initial page is fetched, and doThisAfterResponse() is run for the first time.
We first dump the returned data into our sales array, and then we check if the response has given us a link to the next page (as an indication as to whether or not we're on the final page).
If so, we increment our page count and we make the API call again with the same function to handle the response again.
If not, this means we're at our final page. Now it's time to format the data using R.unnest and finally insert the finalArray of data into our database.
But a funny thing happens here. The entire execution halts at the Meteor.call() and I don't even get an error output to the server logs.
I even tried switching out the Meteor.call() for a simple: Sales.insert({text: 'testing'}) but the exact same behaviour is observed.
What I really need to do is to fetch the information and then store it into the database on the server. How can I make that happen?
EDIT: Please also see this other (much more simplified) SO question I made:
Calling a Meteor Method inside a Promise Callback [Halting w/o Error]
I ended up ditching the NPM package and writing my own API call. I could never figure out how to make my call inside the .then(). Here's the code:
fetchGumroadData: () => {
let sales = [];
const fetchData = (page = 1) => {
let options = {
data: {
access_token: Meteor.settings.gumroadAccessKey,
before: '2099-12-04',
after: '2014-12-04',
page: page,
}
};
HTTP.call('GET', 'https://api.gumroad.com/v2/sales', options, (err,res) => {
if (err) { // API call failed
console.log(err);
throw err;
} else { // API call successful
sales.push(...res.data.sales);
res.data.next_page_url ? fetchData(page + 1) : Meteor.call('addSalesFromAPI', sales);
}
});
};
fetchData(); // run the function to fetch data recursively
}