Array of Promises still say pending after chaining on then function - javascript

I am currently working on a small express app where I am using axios to send/receive data from a github API. I am using insomnia to test sending the data as JSON to the github API in order to find specified user(s) profile information. This is how I am sending the data:
{
"developers": ["person", "person"]
}
Here is my code:
let results = req.body.developers
results.map(async d => {
await axios.get(`https://api.github.com/users/${d}`)
.then((response => {
console.log(response)
}))
.catch((err) => {
console.log(err)
})
})
let out = results.map(r => ({ name: r.data.name, bio: r.data.bio }));
return res.send(JSON.stringify(out));
When I make a request, I receive my promises in an array, but they still say 'Pending'. I thought that by chaining the then function to my get request within the results.map would solve this issue. What is wrong with my code?

You should use await Promise.all() on results, and assign the responses to some variable to use in your out array.
const results = req.body.developers
const responses = await Promise.all(results.map(async d => {
const response = await axios.get(`https://api.github.com/users/${d}`);
console.log(response);
return response;
});
const out = responses.map(({ data } => ({ name: data.name, bio: data.bio }));
return res.send(JSON.stringify(out));
For error handling, you can wrap the Promise.all call in a try/catch block, and handle the rejected promise there.

Related

React: useState(): Fetching data during an ongoing fetch does not work

I want to display a list of the New York Times bestsellers by fetching the data via an API. But, the covers' links have to be fetched from the Google Books API, since the NYT API does not provide them.
Therefore, I want to map a list with the fetched data. In the same step in which I fetch the data from the NYT API, the data from the Google Books API shall be fetched, too. So, I use the await keyword to let the code wait for the second fetch to be finished.
But, unfortunately, the list does not update though the promise has been fulfilled, and the data has been fetched. The bestsellerlist variable is an array of fulfilled promises, but no "real array" I could access.
I guess it should be possible to split the fetch, and create the list out of two arrays: One with the NYT API data, and one with the Google Books API data. Nevertheless, I wonder how I can fix the code, so I can fetch all the data and create the list in one step.
const fetchLinkCover = async (isbn) => {
const res = await fetch(googleApi(isbn));
const json = await res.json();
return json.items[0].volumeInfo.imageLinks.smallThumbnail;
};
useEffect(() => {
fetch(nytimesApi)
.then((res) => res.json())
.then((json) => {
const fetchedBookList = json.results.books;
setBestsellerList(
fetchedBookList.map(async (item) => {
const linkCover = await fetchLinkCover(item.isbns[0].isbn13);
return {
title: item.title,
authors: item.author,
ranking: item.rank,
cover: linkCover,
};
})
);
});
}, []);
console.log(bestsellerList);
console after executing the code
.map doesn't do anything special with promises. If you return a promise from your map function (and async functions always return promises), then .map will create an array of promises. It won't wait for those promises to resolve.
To wait for an array of promises, you can use Promise.all to combine them together into a single promise. Then you either call .then on that promise, or you put your code in an async function and await it.
With .then:
fetch(nytimesApi)
.then((res) => res.json())
.then((json) => {
const fetchedBookList = json.results.books;
const promises = fetchedBookList.map(async (item) => {
const linkCover = await fetchLinkCover(item.isbns[0].isbn13);
return {
title: item.title,
authors: item.author,
ranking: item.rank,
cover: linkCover,
};
});
return Promise.all(promises);
})
.then((books) => {
setBestsellerList(books);
});
Or with async/await:
useEffect(() => {
const fetchBooks = async () => {
const res = await fetch(nytimesApi);
const json = await res.json();
const fetchedBookList = json.results.books;
const promises = fetchedBookList.map(async (item) => {
const linkCover = await fetchLinkCover(item.isbns[0].isbn13);
return {
title: item.title,
authors: item.author,
ranking: item.rank,
cover: linkCover,
};
});
const books = await Promise.all(promises);
setBestsellerList(books);
};
fetchBooks();
}, []);

How to fetch data on every element in an array using array.map method

I want to fetch data for every object in an array and return an array of new objects with the previous and newly fetched data.I got stucked on getting my result array as my function is returning an array of resolved undefined promises.
I am using a flight search api thats using the apca function for fetching
export const searchApcaLocation = async (dataArr,setDeals) => {
const promises = await dataArr.map(async item => {
apca.request(item.destination);
apca.onSuccess = (data) => {
return fetch('http://localhost:3050/googlePlaceSearch',{
method:"post",
headers:{'Content-Type':'application/json'},
body:JSON.stringify({
cityName:data.airports[0].city
})
})
.then(res => res.json())
.then(imagelinkData => {
const locationObject = {
data: item,
imagelink: imagelinkData.link
}
return locationObject
})
.catch(err => console.log('error on image search',err))
};
apca.onError = (data) => {
console.log('error',data)
};
})
const results = await Promise.all(promises)
return results
}
can someone guide me please on what am I doing wrong?
edit:
as I am trying to fix it realized the problem is I am not returning anything in my map function but if trying to return the apca.onSuccess I am getting an array of functions
just return is missing before fetch function. since you're not returning your promise result it's giving undefined.
export const searchApcaLocation = async (dataArr,setDeals) => {
const promises = await dataArr.map(async item => {
apca.request(item.destination);
apca.onSuccess = (data) => {
return fetch('http://localhost:3050/googlePlaceSearch',{
method:"post",
headers:{'Content-Type':'application/json'},
body:JSON.stringify({
cityName:data.airports[0].city
})
})
.then(res => res.json())
.then(imagelinkData => {
const locationObject = {
data: item,
imagelink: imagelinkData.link
}
return locationObject
})
.catch(err => console.log('error on image search',err))
};
apca.onError = (data) => {
console.log('error',data)
};
})
const results = await Promise.all(promises)
return results
}
The issue in your case might be, that you are using async/await and then blocks together.
Let me sum up what is happening :
1) you await dataArray.map
2) within the map callback, you use the onSuccess method of apca
3) within this method you are using then blocks which won't await until you got a response.
At this point where you return the locationObject, your function already reached the return statement and tries to return results.
But results are of course undefined because they never get resolved at all.
Also, keep in mind that your function returns another promise because you used async/await which you have to resolve where you imported it.
Cheers :)

Async functions using value from a promise

So I know this question is asked a lot, but I'm trying to retrieve a variable that is created within a promise. The examples I've seen on here involve calling .then and using the data there, however what I'm trying to do involves an async function--which i cant use within the .then block.
Here's my code. I'm using the Asana API To call out a lists of tasks that are due. It successfuly logs it. But I want to save the list value from the last block as a variable that I can use elsewhere.
const asana = require('asana');
const client = asana.Client.create().useAccessToken("xxx");
client.users.me()
.then(user => {
const userId = user.id;
// The user's "default" workspace is the first one in the list, though
// any user can have multiple workspaces so you can't always assume this
// is the one you want to work with.
const workspaceId = user.workspaces[0].id;
return client.tasks.findAll({
assignee: userId,
workspace: workspaceId,
completed_since: 'now',
opt_fields: 'id,name,assignee_status,completed'
});
})
.then(response => {
// There may be more pages of data, we could stream or return a promise
// to request those here - for now, let's just return the first page
// of items.
return response.data;
})
.filter(task => {
return task.assignee_status === 'today' ||
task.assignee_status === 'new';
})
.then(list => {
console.log (util.inspect(list, {
colors: true,
depth: null
}));
})
.catch(e => {
console.log(e);
});
If you're open to rewriting your .then()'s as async/await something like this could work for you:
const fetch = require('node-fetch');
async function doit() {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const json = await response.json();
console.log(json);
}
doit();

How do I combine two fetch request into the same array?

I am trying to combine two fetch requests in one call so I can get all the data in the same array.
I have tried the Promise.all method but I don't know if it is the right way to do it.
getWeather = async (e) => {
e.preventDefault();
const city = e.target.elements.city.value;
//const api_call = await
const promises = await Promise.all([
fetch(`http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&APPID=${API_KEY}`),
fetch(`http://api.openweathermap.org/data/2.5/forecast?q=${city}&units=metric&APPID=${API_KEY}`)
])
const data = promises.then((results) =>
Promise.all(results.map(r => r.text())))
.then(console.log)
The code actually works and I'm getting data back but I can't understand the json response.
(2) ["{"coord":{"lon":-5.93,"lat":54.6},"weather":[{"id"…7086601},"id":2655984,"name":"Belfast","cod":200}", "{"cod":"200","message":0.0077,"cnt":40,"list":[{"d…on":-5.9301},"country":"GB","population":274770}}"]
How should I set the state?
My state was set like this, with only one call.
if (city) {
this.setState({
temperature: data[0].main.temp,
city: data[0].name,
Is there a better way to do it?
I'd do:
getWeather = async (e) => {
e.preventDefault();
const fetchText = url => fetch(url).then(r => r.json()); // 1
const /*2*/[weather, forecast] = /*3*/ await Promise.all([
fetchText(`.../weather`),
fetchText(`.../forecast`)
]);
this.setState({ temperature: weather.temp, /*...*/ });
}
1: By using a small helper, you don't have to call Promise.all twice. With this both requests are done in parallel (and you should use .json() as you want to parse it as JSON).
2: Through array destructuring you can easily get back the promises results.
3: Through awaiting you get the actual benefit from async functions: You don't need nested .then chains
You can write in following way which is cleaner approach and will have your data categorised
const success = res => res.ok ? res.json() : Promise.resolve({});
const weather = fetch(`http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&APPID=${API_KEY}`)
.then(success);
const forecast = fetch(`http://api.openweathermap.org/data/2.5/forecast?q=${city}&units=metric&APPID=${API_KEY}`)
.then(success);
return Promise.all([weather, forecast])
.then(([weatherData, forecastData]) => {
const weatherRes = weatherData;
const ForecastRes = forecastData; // you can combine it or use it separately
})
.catch(err => console.error(err));
}

Trying to loop axios requests and push it asynchronously

async function getData() {
let getProject =
await axios.get('url', {
auth: {
username: 'username',
password: 'pw'
}
})
let projects = await getProject.data.value;
let arr = []
projects.map(project => {
let item = axios.get(`url`, {
auth: {
username: 'username',
password: 'pw'
}
})
arr.push(item)
console.log('arr', arr)
})
let result = await axios.all(arr)
console.log('pr', result)
return arr;
}
In the getProject I get the object of the projects by calling the API. Then I try to loop through these fetched objects and use unique url for each project to call another API in projects.map.
console.log('arr', arr) gives me the array of Promises, and some of them are failed requests and some of them are successful. This is intended because some projects might not have the valid API. But I will want an array with successful Promises.
This doesn't even reach the line console.log('pr' result) and I am not sure why.
Am I doing it right?
Try revising your code so that arr is an array of functions that return promises for axio requests (rather than arr being an array of actual axios request promises, as you are currently doing):
let projects = await getProject.data.value;
// Map all project items to functions that return promises
let arr = projects.map(project => {
// Return a function that returns a promise
return function() {
// Returns a promise for GET request
return axios.get(`url`, {
auth: {
username: 'username',
password: 'pw'
}
})
}
})
// Axios.all will concurrently perform all GET requests
// in arr (ie the mapping of projects to functions that
// return promises from axios.get )
let result = await axios.all(arr)
// Should print results from axio.get (if all requests successful)
console.log('pr', result)
This is a subtlty with the way methods like axios.all typically work and is similar to the native Promise.all method. For more information see the example just above "axios API" in the axios docs

Categories

Resources