Reputation: 53
I have an array of data objects about people. Each person object includes 0-n URLs for additional info (guests of the person).
I want to process this list, calling each of the 'guest' URLs and including the guest's names in the original data set.
Context: this is an AWS lambda function. I'm using lambda-local to run this locally. (lambda-local -l index.js -e fixtures/test_event1.json
).
I'm successfully using await/async to retrieve the initial set of data.
But I'm unable to get if working for these further calls about guest info. It always shows a pending Promise, even though the result is awaited.
// index.js
const fetch = require('node-fetch');
exports.handler = async function(event){
try {
let registrations = await getEventRegistrations();
console.log(registrations);
/* All good here - sample console output
[ { contactId: 43452967,
displayName: 'aaa, bbb',
numGuests: 0,
guestUrls: [] },
{ contactId: 43766365,
displayName: 'bbb, ccc',
numGuests: 1,
guestUrls:
[ 'https://<URL>' ] },
{ contactId: 43766359,
displayName: 'ccc, ddd',
numGuests: 2,
guestUrls:
[ 'https://<URL>',
'https://<URL> ] } ]
*/
// Expanding the guest URLs is proving problematic - see expandGuests function below
registrations = registrations.map(expandGuests);
console.log(registrations);
/* Registrations are just pending Promises here, not the real data
[ Promise { <pending> },
Promise { <pending> },
Promise { <pending> } ]
*/
return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify(registrations),
};
}
catch (exception) {
console.log(exception);
return {
statusCode: 500,
body: 'Unable to retrieve data.'
};
}
};
function getEventRegistrations() {
return fetch('<URL>')
.then(res => res.json())
.catch(function (error) {
console.log('Event registrants request failed', error);
return null;
});
}
function getGuestName(url) {
return fetch(url)
.then(res => res.json())
.then(guest => guest.DisplayName)
.catch(function (error) {
console.log('Guest name request failed', error);
return null;
});
}
async function expandGuests(data) {
const promises = data.guestUrls.map(url => getGuestName(url));
data.guestNames = await Promise.all(promises);
return data;
}
How can I resolve these pending Promises and thus return useful data?
Thank you.
Upvotes: 4
Views: 2431
Reputation: 488
The comments are correct in pointing out that mapping an async
function will return an array of Promises. What they don't explicitly mention is that you have two maps, and which one is problematic.
The issue is in the line:
reservations = reservations.map(expandGuests);
Any time you use map, you'll need to actually resolve the promises that return.
So:
const mappedPromises = reservations.map(expandGuests); //returns an Array of Pending promises
const mappedReservations = await Promise.all(mappedPromises); //resolves the promises
Upvotes: 0
Reputation: 84902
array.map doesn't contain any logic to wait for promises. It just calls the function for each element of the array, and then synchronously produces an array of the results. If you pass in async functions, then the result be an array of promises (since all async functions return promises).
You'll need to use Promise.all
to create a promise which will resolve once all the individual promises have resolved, and then await
that.
const promises = data.guestUrls.map(url => getGuestName(url));
data.guestNames = await Promise.all(promises);
P.S, i strongly recommend against mixing async/await with .then. The cases where you actually need to do so are very rare, so most of the time you just make the code harder to understand. For example, this function:
async function getGuestName(url) {
return await fetch(url)
.then(res => res.json())
.then(guest => guest.DisplayName)
.catch(function (error) {
console.log('Guest name request failed', error);
return null;
});
}
Should either be done like this:
async function getGuestName(url) {
try {
const res = await fetch(url);
const guest = await res.json();
return guest.DisplayName;
} catch (error) {
console.log('Guest name request failed', error);
return null;
}
}
Or like this
function getGuestName(url) {
return fetch(url)
.then(res => res.json())
.then(guest => guest.DisplayName)
.catch(function (error) {
console.log('Guest name request failed', error);
return null;
});
}
Upvotes: 6