我有一个来自Spotify的JSON文件,其中包含代表艺术家的对象。在这些对象内部,属性之一是“流派”,即字符串(流派)的数组。
我想做的是在MongoDB中找到或创建这些类型。然后将这些对象ID添加到数组中,并在创建Artist对象时传递该数组。
但是,我的Artist是在完成其流派的查找或创建过程之前以DB方式创建的。
因此,fillRest行在fillGenres之前运行。
我尝试将Object.values.forEach更改为for..of,for..in,使用不同的异步,promise,调制代码...
基本上可以等待的地方(可能在错误的地方)
import * as data from '../spotify_data/artist_id.json';
async function fillGenres(array) {
const genreIDs = []; // array of genre object IDs I'm trying to put inside every appropriate artist
if (array !== 'undefined') {
for (const element of array) {
await Genre.findOrCreate({ name: element }, (err, result) => {
genreIDs.push(result._id);
});
}
}
return genreIDs;
}
async function fillRest(entry, genreIDs) {
const artist = {
name: entry.ArtistName,
genres: genreIDs,
spotifyID: entry.ArtistID,
popularity: entry.Popularity,
spotifyFollowers: entry.Followers,
};
Artist.create([artist])
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(error);
});
}
async function spotifySeed() {
const entries = Object.values(data);
for (const entry of entries) {
fillGenres(entry.Genres)
.then((genreIDs) => {
fillRest(entry, genreIDs); // this line gets executed before fillGenres above^ which is super weird
});
}
}
spotifySeed();
艺术家被添加到MongoDB,其流派设置为[]。 之后,我得到带有良好genreID数组的控制台输出(应该在其中^而不是流派)。
已解决-编辑
感谢所有提供帮助的人。问题出在findOrCreate中,因为它没有返回承诺。我将此包用于具有Promises(https://www.npmjs.com/package/mongoose-findorcreate)的猫鼬。
现在的代码是
if (Array.isArray(array)) {
// eslint-disable-next-line no-restricted-syntax
for (const element of array) {
await Genre.findOrCreate({ name: element })
.then((result) => {
genreIDs.push(result.doc._id);
});
}
}
和SpotifySeed
const genreIDs = await fillGenres(entry.Genres);
await fillRest(entry, genreIDs);
答案 0 :(得分:3)
我以前没有使用过Spotify API,所以我对此不多说,但是乍看之下有几个问题。首先,您要检查if (array !== 'undefined') {
,这是在检查array
变量是否是字面上是'undefined'
(不是值undefined
)的字符串。我可以肯定这不是您想要的。如果您要确保Array.isArray(array)
实际上是一个数组,最好在这里使用array
。
第二,您将异步功能和Promises混合使用,通常不应该使用(imo)。您应该使用其中一个,所以它是一致的并且易于遵循。如果您使用await
而不是.then
,则可以更“同步”地编写它,并且它应该更易于理解。
import * as data from '../spotify_data/artist_id.json';
async function fillGenres(array) {
const genreIDs = []; // array of genre object IDs I'm trying to put inside every appropriate artist
if (Array.isArray(array)) {
for (const element of array) {
const result = await Genre.findOrCreate({ name: element });
genreIDs.push(result._id);
}
}
return genreIDs;
}
async function fillRest(entry, genreIDs) {
const artist = {
name: entry.ArtistName,
genres: genreIDs,
spotifyID: entry.ArtistID,
popularity: entry.Popularity,
spotifyFollowers: entry.Followers,
};
try {
const result = await Artist.create([artist]);
console.log(result);
} catch (error) {
console.log(error);
}
}
async function spotifySeed() {
const entries = Object.values(data);
for (const entry of entries) {
const genreIDs = await fillGenres(entry.Genres);
await fillRest(entry, genreIDs);
}
}
await spotifySeed();
答案 1 :(得分:1)
我对Spotify API一无所知,所以这只是一个猜测。在fillGenres
中,您拥有:
await Genre.findOrCreate({ name: element }, (err, result) => {
genreIDs.push(result._id);
});
您正在传递一个回调函数。有时,库会允许您使用promises 或回调。如果您传递回调,它将不会返回承诺。因此,我猜想循环会从对Genre.findOrCreate
的所有调用开始,由于您使用的是回调,因此不会返回promise。然后立即返回。然后fillRest
被调用,并可能在所有Genre.findOrCreate
调用之前完成。
您想要类似的东西
const result = await Genre.findOrCreate({ name: element });
genreIDs.push(result._id)
更好的是:
function fillGenres(genreNames) {
if(!genreNames || !genreNames.length) return Promise.resolve([])
return Promise.all(genreNames.map(name => {
return Genre.findOrCreate({ name })
.then(result => result._id)
})
}
这将同时运行所有流派调用,并在完成后返回所有流派调用,而不是等待一个接一个地添加(如您的for循环)。
如果Genre.findOrCreate没有返回Promise,则可以创建一个包含以下内容的版本:
function genreFindOrCreate(data) {
return new Promise((resolve, reject) => {
Genre.findOrCreate(data, (err, result) => {
if(err) reject(err)
else resolve(result)
})
})
}