I'm currently working on writing a function on AWS Lambda. I want to convert a RSS feed into a JSON and give that as a response in the body when making to the Lambda endpoint.
I'm using an npm package to convert the RSS to JSON. However, when I run the code. I see that I get undefined in the conversion of the RSS URL. Here is the following code:
const feed = require('rss-to-json');
exports.handler = async (event) => {
let rssFeed = event.queryStringParameters.rssFeed;
let rssAsJsonData = convertRssIntoJson(rssFeed);
return sendRes(200, rssAsJsonData);
};
const sendRes = (status, body) => {
var response = {
isBase64Encoded: true|false,
statusCode: status,
headers: {
"Content-Type": "application/json"
},
body: body,
};
return response;
};
function convertRssIntoJson (rssFeed) {
console.log(rssFeed);
return feed.load(rssFeed, function(err, rss){
if(err) {
console.log("Error: ${err}");
return;
}
console.log(rss)
return rss;
});
};
However, in the logs I get undefined when console.log(rssAsJsonData).
However, when debugging I was able to see console.log(rss) working when I change body to be body: json.stringify("TESTING")
However, it only worked when logging to the console not when I tried to pass it to the body body: body, I can't seem to find what the error is. I'm moving from Ruby to JavaScript for this project maybe I'm missing something.
I'm using Postman to make the calls:
function convertRssIntoJson (rssFeed) {
console.log(rssFeed);
return feed.load(rssFeed, function(err, rss){
if(err) {
console.log("Error: ${err}");
return;
}
console.log(rss)
return rss;
});
};
The piece of code above is a callback. Under the hood, feed.load is asynchronous, which makes your callback be executed asynchronously.
Now, when you invoke your function like this
let rssAsJsonData = convertRssIntoJson(rssFeed);
your rss object inside convertRssIntoJson does not hold any value yet, because the callback hasn't been populated up to now. This is where your undefined comes from.
Callbacks themselves don't make code asynchronous by default, but NodeJS works with a non-blocking IO model and, since feed.load is an IO call, it will be executed asynchronously.
You have a few options now, but I will list only two. A not-so-nice and a nice solution:
1) The not-so-nice way to fix it is to add a callback as argument to your convertRssIntoJson function and pass the value of that rss object upstream. The not-so-nice full code can be found below:
const feed = require('rss-to-json');
exports.handler = async (event) => {
let rssFeed = event.queryStringParameters.rssFeed;
convertRssIntoJson(rssFeed, (err, data) => {
if (err) {
return sendRes(500, { message: 'There was an err: ' + err.message })
}
return sendRes(200, data)
})
};
const sendRes = (status, body) => {
var response = {
isBase64Encoded: true | false,
statusCode: status,
headers: {
"Content-Type": "application/json"
},
body: body,
};
return response;
};
const convertRssIntoJson = (rssFeed, callback) => {
console.log(rssFeed);
feed.load(rssFeed, function (err, rss) {
if (err) {
console.log("Error: ${err}");
callback(err, undefined)
}
console.log(rss)
callback(undefined, rss)
});
};
2) The nice, clean, elegant and recommended solution is this one. Wrap your callback in a Promise, like this
function convertRssIntoJson(rssFeed) {
console.log(rssFeed);
return new Promise((res, rej) => {
feed.load(rssFeed, function (err, rss) {
if (err) {
console.log("Error: ${err}");
return rej(err)
}
console.log(rss)
return res(rss)
});
})
};
Since your handler is async, it means it can just await on Promises.
So your client code is now as simple as:
return sendRes(200, await convertRssIntoJson(rssFeed));
Your final code will look like (I have refactored a little bit to make use of arrow functions):
const feed = require('rss-to-json');
exports.handler = async (event) => {
let rssFeed = event.queryStringParameters.rssFeed;
return sendRes(200, await convertRssIntoJson(rssFeed));
};
const sendRes = (status, body) => {
var response = {
isBase64Encoded: true | false,
statusCode: status,
headers: {
"Content-Type": "application/json"
},
body: body,
};
return response;
};
const convertRssIntoJson = (rssFeed) => {
console.log(rssFeed);
return new Promise((res, rej) => {
feed.load(rssFeed, (err, rss) => {
if (err) {
console.log("Error: ${err}");
return rej(err)
}
console.log(rss)
return res(rss)
});
})
};
If you want to know more about async/await, you can see it in here.
EDIT: Code refactor and code added for solution 1)
Related
There is a mocha test as follows:
it('(t4) should assert that issues a GET request by john should fetch his own records via svc1', done => {
let filter = { where: { key: 'john' }};
let testHelperUrl = `${svc1}/api/TestHelper?filter=${encodeURIComponent(JSON.stringify(filter))}`;
let fetchToken = cb => {
req('GET', testHelperUrl, (err, resp) => {
if(err) {
return cb(err);
}
cb(null, resp.data[0]);
});
};
let getWithToken = ({ value: token }, cb) => {
let consumerUrl = `${svc1}/api/Consumers?access_token=${token}`;
req('GET', consumerUrl, (err, resp) => {
if(err) {
return cb(err);
}
cb(null, resp.data);
})
};
async.seq(fetchToken, getWithToken)((err, data) => {
if(err) {
return done(err);
}
expect(data.length).to.equal(2);
done();
});
});
It calls some APIs via the req(), and, it is defined as follows:
const req = (method, url, data, cb) => {
if (method.toLowerCase() === 'get') {
if (typeof cb === 'undefined') {
cb = data;
data = null;
}
(async () => {
try {
let response = await got(url, { responseType: 'json', https: { rejectUnauthorized: false } });
cb(null, { code: response.statusCode, data: response.body });
} catch (err) {
cb(err);
}
})();
}
else {
(async () => {
try {
let response = await got.post(url, { json: data, responseType: 'json', https: { rejectUnauthorized: false } });
cb(null, { code: response.statusCode, data: response.body });
} catch (err) {
cb(err);
}
})();
}
};
The above code uses got for doing http requests. Its design is promised-based, and hence, the reason to why async/await pattern is used here.
I am also using the async library here. The following line: expect(data.length).to.equal(2) should fail, and, the expectation is that mocha should report that error correctly.
However, the error is actually trapped by the async library. It should report an error saying callback already was called. But instead the program just dies. However, through debugging I am able to confirm that it was an error trapped in the async module. No idea why it dies abruptly though.
Am I doing something wrong? Or should I modify mocha code to properly handle the assertion error like wrapping that in a try/catch block or something?
Update:
Solution is to invoke the callback function to req() inside of a process.nextTick(). E.g.
try {
let response = await got(url, { responseType: 'json', https: { rejectUnauthorized: false } });
process.nextTick(() => cb(null, { code: response.statusCode, data: response.body }));
} catch (err) {
process.nextTick(() => cb(err));
}
In short: I need to request a token from Auth0 in order to then retrieve user information (all inside a Vue plugin that is called by Vuex).
I currently have two classes - one for dealing with the token and the other for actually retrieving the profile.
export class Token {
constructor () {
this.options = { method: 'POST',
url: 'https://.auth0.com/oauth/token',
headers: { 'content-type': 'application/json' },
body: '{"client_id":"","client_secret":"","audience":"","grant_type":"client_credentials"}' }
}
async getToken () {
try {
this.request = request(this.options, function (error, response, body) {
if (error) {
console.error(error)
} else {
let parsed = JSON.parse(body)
let token = parsed.access_token
return token
}
})
let blah = await this.request
console.log(blah)
} catch (err) {
console.error(err)
}
}
}
export default class Profile {
constructor () {
const token = new Token()
let result = token.getToken()
//console.log(result)
this.auth0Manage = new auth0.Management({
domain: '.auth0.com',
token: `${result}`
})
}
getProfile () {
return new Promise((resolve, reject) => {
let idToken = localStorage.getItem('id_token')
let decoded = jwt_decode(idToken)
let userId = decoded.sub
this.auth0Manage.getUser(userId, function (err, data) {
if (err) {
console.error(err)
}
resolve(data)
})
})
}
}
I am expecting the following flow:
Get token > Get profile. It doesn't have to be in two classes (originally it wasn't but I couldn't instantiate the constructor for Profile without getting the token first, which would have to be its own class (afaik). Thanks!
Looks like you are not awaiting your getToken result in the profile constructor. Problem is, constructors are always synchronous. So in order to await the token, you need to do something like make Profile#auth0manage an async value and await that. Maybe like:
export default class Profile {
constructor () {
let token = new Token()
this.auth0Manage = new Promise((resolve, reject) => {
token.getToken().then((result) => {
return new auth0.Management({
domain: '.auth0.com',
token: `${result}`
})
}).then(resolve, reject)
})
}
async getProfile () {
let auth0Manage = await this.auth0Manage
return new Promise((resolve, reject) => {
let idToken = localStorage.getItem('id_token')
let decoded = jwt_decode(idToken)
let userId = decoded.sub
auth0Manage.getUser(userId, function (err, data) {
if (err) {
console.error(err)
}
resolve(data)
})
})
}
}
Caveats of this are that if you get an error in getToken then every call to getProfile will return the same error. So you may want to handle that somehow. Also, you have to remember to await every use of this.auth0Manage. Ideally, you could do something like pass in the auth0Manager to the constructor of Profile, so that you won't event try to make a profile until the token has been fetched. Tends to work out better to do things that way.
In my code I try to assign a value to json variable to return it after (because I can't return it from the anon. function).
As my function is async, because it sends requests (maybe someone knows how to make it sync? I didn't plan to make it asynchronous), I've added await before the request (https.get).
I've been trying to get value from the Promise, but it's always undefined, even though I've awaited the async function.
Here's a code:
async function get_users() {
const https = require('https');
var token = '...';
var json = undefined;
await https.get('...', (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
json = JSON.parse(data)['response']['items'];
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
return json;
}
get_users().then(function(result) {
console.log(result);
});
Return a Promise and resolve it, when the end event is called, otherwise reject it in case of an error occurred:
async function get_users() {
const https = require('https');
const token = '...';
return new Promise((resolve, reject) => {
https.get('...', resp => {
let data = '';
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
let json;
try {
json = JSON.parse(data)['response']['items'];
} catch (e) {
reject(e);
};
resolve(json);
});
}).on("error", err => reject(err));
});
}
get_users().then(result => console.log(result));
Please refer my below code.I had issues with getting responses from Promises too.But i finally got it to work.Here's the code:
var output;
var rp = require('request-promise-native');
var myJSONObject = {
"inputs": [{
"name": "<name>",
"value": < value >
}]
};
var orchName = 'TEST05';
postData = JSON.stringify(myJSONObject);
return networkCall(postData, orchName).then((response) => {
console.log('response is' + response)
}).catch((response) => {
console.log(`ERROR: ` + response);
});
function networkCall(postData, orchName) {
return new Promise((resolve, reject) => {
var options = {
method: 'post',
uri: '<URL>',
body: postData,
auth: {
'user': 'usr',
'pass': 'pwd'
},
json: true
};
return rp(options)
.then(body => {
var response = body;
resolve(response);
})
.catch(err => {
console.log('FAILED' + err);
reject(err);
});
});
}
This way your code can run in Synchronous Flow.If the return value is undefined,then,what might have probably happened is that the calling function would have finished executing even before the called function returns its response.But the above approach would work just fine.
Objective
Disclaimer: I am new to node world and having tough time wrapping head around node asynchronous behaviour.
I am trying to write a wrapper function to do a https.get on a given url and return json output.
Code
const https = require('https');
// Get the user details
var myUrl = <valid-url>;
const getJson = function(url) {
// https get request
const req = https.get(url, (res) => {
// get the status code
const { statusCode } = res;
const contentType = res.headers['content-type'];
// check for the errors
let error;
if (statusCode !== 200) {
error = new Error('Request Failed.\n' +
`Status Code: ${statusCode}`);
} else if (!/^application\/json/.test(contentType)) {
error = new Error('Invalid content-type.\n' +
`Expected application/json but received ${contentType}`);
}
if (error) {
console.error(error.message);
// consume response data to free up memory
res.resume();
return;
}
//parse json
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
console.log(parsedData);
} catch (e) {
console.error(e.message);
}
});
}).on('error', (e) => {
console.error(`Got error: ${e.message}`);
});
}
console.log(getJson(myUrl));
Output
undefined
{ user_id: <user-id>,
name: 'Ajay Krishna Teja',
email: <my-email> }
Issue
So the https.get is able to hit end point and get data but not able to return the json. Constantly returning Undefined.
Things I tried
Returning parsedData on res.on(end) block
Defining a var and copying parsedData
Copying to a global variable (although I knew it's very bad practice)
Places I looked up
Node.js variable declaration and scope
How to get data out of a Node.js http get request
Javascript function returning undefined value in node js
Updated: Working code
const getJson = function(url,callback) {
// https get request
const req = https.get(url, (res) => {
// get the status code
const { statusCode } = res;
const contentType = res.headers['content-type'];
// check for the errors
let error;
if (statusCode !== 200) {
error = new Error('Request Failed.\n' +
`Status Code: ${statusCode}`);
} else if (!/^application\/json/.test(contentType)) {
error = new Error('Invalid content-type.\n' +
`Expected application/json but received ${contentType}`);
}
if (error) {
console.error(error.message);
// consume response data to free up memory
res.resume();
return;
}
//parse json
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
callback(parsedData);
} catch (e) {
callback(false);
console.error(e.message);
}
});
}).on('error', (e) => {
console.error(`Got error: ${e.message}`);
});
return req;
}
// calling
getJson(amznProfileURL,(res) => {
console.log(res);
});
Short answer: You are not returning anything in your getJson function and undefined is the default Node/Javascript return value.
function getJson(){
callAsyncFunction(param1, param2, param3)
// there is no return value!
}
Longer answer: Javascript (and Node as a result) is a single threaded language that uses callbacks as it's mechanism to return async results back to the callee. To do this, you pass a function into asynchronous functions as a parameter and then that function gets called at some point in the future whenever the asynchronous function is ready to send back it's result. Calling return from this "anonymous function" is actually just returning from the "callback" function you are sending into the async function.
function getJson(){
console.log('A')
// request is started, but getJson continues execution!
http.get(url, (res)=> {
console.log('C') // by the time I'm called, 'B' has already been printed and the function has returned!
return true // this won't return getJson! It will only return the callback function which doesn't do anything!
})
console.log('B')
// end of function without return value, return undefined!
}
// Will print 'A', 'B', 'C'
There are a couple different ways you can handle this. Callbacks have been used traditionally but Javascript also natively supports Promises which are a little easier to manage and are used in many popular frameworks by default.
You can implement your function with callbacks by providing your own callback parameter to call as soon as http.get returns itself.
// define getJson with second callback parameter
const getJson = function(url, callback) {
http.get(url, (res) => {
if(res){
callback(res) // result came back, send to your own callback function
} else {
callback(false) // request failed, send back false to signify failure
}
})
}
// now I can use getJson and get the result!
getJson('http://getjson.com', (res) => {
console.log('got result!', res)
})
This is a pretty common hump to get over with async functions in node (and javascript in general).
What's happening is that your console.log(getJson(myUrl)) is called before the http request has returned anything. Basically, things like this won't work with async functions.
If you put your console.log() inside res.on('end) it will work. The way you need to deal with this if either put all your logic in the res.on('end) which kind of sucks, or pass a callback to your getJson() function which you call in res.on('end'), or wrap everything in a promise, which you can return from getJson().
To use a callback you would do something like this:
const getJson = function(url, callback) {
// a bunch of code
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
callback(null, parsedDate) // callbacks in node traditionaly pass an error as the first arg
}
//finish
}
The you call it with a function:
getJson(url, function(err, return_val) {
if (err) // handle error
console.log(return_val)
}
You can also look at other HTTP libraries like Axios that will return a promise without much work. With axios and similar libraries you can simply:
axios.get(url)
.then(response => {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
Which is one of the reasons people use these libraries. More here: https://github.com/axios/axios
Because it runs asynchronously, it does not wait for the function call to end.
You can fix it with promise pattern.
Try something like this:
/**
* Created by bagjeongtae on 2017. 10. 2..
*/
function parseData(url) {
return new Promise((resolve, reject) => {
https.get(url, (res) => {
// get the status code
const {statusCode} = res;
const contentType = res.headers['content-type'];
// check for the errors
let error;
if (statusCode !== 200) {
reject('Request Failed.\n' + `Status Code: ${statusCode}`);
} else if (!/^application\/json/.test(contentType)) {
reject('Invalid content-type.\n' +
`Expected application/json but received ${contentType}`);
}
if (error) {
console.error(error.message);
reject(error.messag);
}
res.resume();
//parse json
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk) => {
rawData += chunk;
});
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
console.log(parsedData);
resolve(parseData);
} catch (e) {
console.error(e.message);
reject(e.messag);
}
});
});
});
};
parseData('http://www.example.com').then( result =>{
console.log(result);
}, err => {
console.log(err);
})
Running getJson from console.log is asynchronous, so it does not wait for getJson to finish.
Asynchronous can be used like a synchronous.
I think the output is correct.The getJson(myUrl) is return undefined since you not set a return in the getJson function,the javascript return undefined by default and the
{ user_id: <user-id>,
name: 'Ajay Krishna Teja',
email: <my-email> }
is the output by console.log(parsedData) in you code.
I am currently trying to fetch data from the Spotify API with promises, and yesterday I got tremendous help for another question, regarding the same topic: "Loop the object returned from node promise and feed to the next .then".
What I do is first getting the tracks from my playlist, and then call another api which fetches the artists. Lastly I call another api which gets the artist images.
Now my question is: how do I return the data that I get from my promises?
This is my function that gets the playlist urls:
function getPlaylists(access_token) {
var options = {
url: 'https://api.spotify.com/v1/me/playlists',
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};
return new Promise(function(resolve, reject) {
request.get(options, function(error, response, body) {
var playlists = body.items;
var playlistArray = [];
playlists.forEach(function(playlist) {
var name = playlist.name;
var url = playlist.tracks.href;
playlistArray.push(url);
});
if(!error) {
resolve(playlistArray);
} else {
reject(error);
}
});
});
}
This one gets the artists:
function getArtists(url,access_token) {
var params = {
url: url,
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};
return new Promise(function(resolve, reject) {
request.get(params, function(error, response, body) {
var tracks = body.items;
var artistArray = [];
tracks.forEach(function(artists) {
let allArtists = artists.track.artists;
allArtists.forEach(function(artist) {
artistArray.push(artist);
});
})
if(!error) {
resolve(artistArray);
} else {
reject(error);
}
});
})
}
And this one gets the artist image:
function getArtistImages(artistId) {
var options = {
url: 'https://api.spotify.com/v1/artists/' + artistId,
json: true
};
return new Promise(function(resolve, reject) {
request.get(options, function(error, response, body) {
if(error != null) {
reject(error);
} else {
resolve(body);
}
});
})
}
EDIT EDIT
The way I call these functions is like this:
getPlaylists(access_token)
.then(playlists => Promise.all(playlists.map(playlist =>
getArtists(playlist, access_token)))
.then(artists => {
artists.map(artist => {
artist.map(a => {
console.log(a);
let component = renderToString(
<App>
<Table artists={a} />
</App>
);
res.send(
component
)
})
})
}));
It only returns the first result - obviously because it only gets to loop through the forEach loop once, before "res.send()", so how do I make sure that it loops through all artists, before I render the view? I believe I have to do another Promise.all(), but I am not sure where - does anyone have an idea?
I appreciate it :)
Its old post but I think it can be helpfull for someone.
I wrote a plugin to perform foreach based on promises supporting concurrency. I see you dont need concurrency what turn things more simple to apply other solutions.
I wrote a code by your code using my plugin. It works!
'use strict';
var request = require('request')
var promiseForeach = require('promise-foreach')
function getPlaylists(access_token) {
var options = {
url: 'https://api.spotify.com/v1/me/playlists',
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};
return new Promise(function (resolve, reject) {
request.get(options, function (error, response, body) {
var playlists = body.items;
var playlistArray = [];
playlists.forEach(function (playlist) {
var name = playlist.name;
var url = playlist.tracks.href;
playlistArray.push(url);
});
if (!error) {
resolve(playlistArray);
} else {
reject(error);
}
});
});
}
function getArtists(url, access_token) {
var params = {
url: url,
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};
return new Promise(function (resolve, reject) {
request.get(params, function (error, response, body) {
var tracks = body.items;
var artistArray = [];
tracks.forEach(function (artists) {
let allArtists = artists.track.artists;
allArtists.forEach(function (artist) {
artistArray.push(artist);
});
})
if (!error) {
promiseForeach.each(artistArray,
[function (artist) {
return getArtistImages(artist.id)
}],
function (arrayOfResultOfTask, currentList) {
return {
artistId: currentList.id,
artistName: currentList.name,
artistImages: arrayOfResultOfTask[0].images
}
},
function (err, newList) {
if (err) {
console.error(err)
return;
}
resolve(newList)
})
} else {
reject(error);
}
});
})
}
function getArtistImages(artistId) {
var options = {
url: 'https://api.spotify.com/v1/artists/' + artistId,
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};
return new Promise(function (resolve, reject) {
request.get(options, function (error, response, body) {
if (error != null) {
reject(error);
} else {
resolve(body);
}
});
})
}
var access_token = 'YOUR-TOKEN';
getPlaylists(access_token)
.then(playlists => {
promiseForeach.each(playlists,
[function (playlist) {
return getArtists(playlist, access_token)
}],
function (arrayOfResultOfTask, currentList) {
return {
playlistURL: currentList,
artist: arrayOfResultOfTask[0]
}
//return renderToString(
// <App>
// <Table artists={render} />
// </App>
//);
},
function (err, newList) {
if (err) {
console.error(err)
return;
}
res.send(newList)
})
});
The plugin: https://www.npmjs.com/package/promise-foreach
I hope it helps someone!
If I understand your problem correctly, it sounds like you are tripping up on how to actually use the result of Promises.
Asynchronous applications:
Promises encapsulate an asynchronous result, which is made available through the callback passed to then().
This asynchronous-ism will cascade throughout your application.
There are a number of ways applications manage the reality of asynchronous results: events, callbacks, observables, promises...
Example (using events):
This is a crude and untested example of how the data from an asynchronous request can get injected into your view. When my asynchronous request calls my then() callback, I update my model which triggers an event to re-render my view.
There are totally issues with this example (i.e. what if I don't want to rerender my whole view? what if my getArtists() returns sooner than my view can render it's loading state?). But for simplicity, we won't go there.
+function(){
var _view = $('#viewport');
var _model = {...}
var _spotifyClient = new SpotifyClient(); // this contains method similar to those you included in your question
_view.on('load', onLoad);
_view.on('model:update', onModelUpdate);
function onLoad() {
_spotifyClient
.getArtists()
.then(function(result) {
// when the getArtists() request has responded, I can update my model.
updateModel({ isLoading: false, artists: result });
})
// this will happen immediately after starting the "getArtists()" request.
udpateModel({ isLoading: true });
}
function updateModel(mod) {
for(var p in mod) {
_model[p] = mod[p];
}
_view.trigger('model:update', _model);
}
function onModelUpdate(model) {
refreshView();
}
function refreshView() {
var viewModel = buildTemplateModel(_model);
renderTemplate(_view, viewModel);
}
}();
I encourage you to research some view frameworks such as angular, knockout, or react. I also encourage you to research Reactive Extensions which provides an observable interface and many utilities regarding asynchronous streams.
Note on side effects:
Having the result of a promise trigger an event can be classified as a "side-effect". You should keep side-effects to a minimum in our application, and they really only belong in the controller / main part of your application.
If you are using promises in your application, reusable library classes and functions, that operate on promises, should return promises.
You need to call res.send on the array of all results, not on each one separately:
getPlaylists(access_token).then(playlists =>
Promise.all(playlists.map(playlist =>
getArtists(playlist, access_token)
))
).then(artists => {
res.send(artists.map(artist =>
artist.map(a => {
console.log(a);
return renderToString(
<App>
<Table artists={a} />
</App>
);
})
))
});
Also your parenthesis were slightly misnested (didn't match the indentation of the then calls) but that didn't lead to the problem.