问题
I'm learning Node JS as well as how to access an API's information, and I've come across a problem where when I push values to an array, the info never gets added globally.
So let's say I have var albIds= new Array(5)
, the values never get added. Through looking at this and this I now know that the issue is that the information never gets into the array by the time I call it, due to the synchronous code being done first, however, I am confused at how to make the synchronous wait for the asynchronous to complete.
my code so far.
//prints out albums 1-5 id's
var id = '3TVXtAsR1Inumwj472S9r4';
var albIds= new Array(5);
async function getAlbums(i) {
var authOptions = {
url: 'https://accounts.spotify.com/api/token',
headers: {
'Authorization': 'Basic ' + (new Buffer.from(client_id + ':' + client_secret).toString('base64'))
},
form: {
grant_type: 'client_credentials'
},
json: true
};
request.post(authOptions, function(error, response, body) {
if (!error && response.statusCode === 200) {
// use the access token to access the Spotify Web API
// const querystring = require('querystring');
const data = {
market: "US",
limit: "5"
};
const q = querystring.stringify(data);
var token = body.access_token;
var options = {
url: 'https://api.spotify.com/v1/artists/'+id+'/albums?'+q,
headers: {
'Authorization': 'Bearer ' + token
},
json: true
};
request.get(options, function(error, response, body)
{
for(var i = 0; i<5;i++)
{
albIds.push(body.items[i].id);
}
});
}
})
};
async function fetchUsers()
{
for(var i = 0;i<5;i++)
{
await getAlbums(i);
console.log(albIds[i]);
}
}
fetchUsers();
Any help appreciated. For reference, I'm using Spotify's API
回答1:
FYI, albIds
is NOT a global. It's a module scoped variable.
Then, you need to understand that await
ONLY does something useful if you are awaiting a promise that is connected to the completion of your asynchronous operation. That is NOT the case for your getAlbums()
function. While it's declared async
so it does return a promise, that promise is not connected at all to the completion of your asynchronous operations.
Instead, the promise completes, long before you've put any data into the albIds
array. Thus, you think the array is empty (because nothing has yet been put into it when you're trying to use it).
Here would be my suggestion:
const rp = require('request-promise');
//prints out albums 1-5 id's
const id = '3TVXtAsR1Inumwj472S9r4';
async function getAlbums(i) {
const albIds = new Array(5);
const authOptions = {
url: 'https://accounts.spotify.com/api/token',
headers: {'Authorization': 'Basic ' + (new Buffer.from(client_id + ':' + client_secret).toString('base64'))},
form: {grant_type: 'client_credentials'},
json: true
};
const body = await rp.post(authOptions, function(error, response, body) {
const data = {
market: "US",
limit: "5"
};
const q = querystring.stringify(data);
const token = body.access_token;
const options = {
url: 'https://api.spotify.com/v1/artists/' + id + '/albums?' + q,
headers: { 'Authorization': 'Bearer ' + token },
json: true
};
let result = await rp.get(options);
for (let j = 0; j < 5; j++) {
albIds.push(result.items[j].id);
}
// let resolved value be the array
return albIds;
};
async function fetchUsers() {
for (let i = 0; i < 5; i++) {
const albiIds = await getAlbums(i);
console.log(albIds[i]);
}
}
fetchUsers();
Summary of Changes:
- Switch to the request-promise library that uses promises to communicate results and checks for a 2xx status automatically. This is much more compatible with an
async
function and we can useawait
with it. - Make the
albIds
be the resolved value ofgetAlbums()
rather than a higher scoped, shared variable (much more immune to concurrency issues and it ensures you can't attempt to use it before it's ready). - In
fetchUsers()
, use thealbIds
that comes from the promise returned bygetAlbums()
. - Change all variables to
let
orconst
as appropriate (no need to usevar
here at all). - Use
await
on the request-promise calls to simplify the code flow and to be compatible with the promise thatgetAlbums()
is returning.
FYI, if this is fairly new code, I would stop using the request
or request-promise
libraries as they are officially deprecated for new development. While they will be maintained, they will not be getting new features added to them over time. You can read why this is happening, but basically they've been around so long and node.js has changed so much during that time that they've become a bit of a mess to maintain and move forward. But, there are so many users that they can't really make the breaking changes to the API that they would have to in order to really continue to move it forward (like make it entirely promise-driven). And, since there are a number of good alternatives that have a more modern design, they've decided to turn the future over to the other alternatives. I'm personally using got()
for most of my uses, but there are numerous other alternatives to choose from.
See Should I use the 'request' module for a new project? for more info.
来源:https://stackoverflow.com/questions/60678479/how-to-go-about-adding-asynchronous-information-to-a-global-array