内部的代码,然后在Promise之前执行

时间:2019-05-22 00:30:11

标签: javascript asynchronous mongoose ecmascript-6 promise

我有一个来自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);

2 个答案:

答案 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)
    })
  })
}