Код внутри. Затем выполняется перед Обещанием - PullRequest
3 голосов
/ 22 мая 2019

У меня есть файл JSON из Spotify с объектами, которые представляют художников. Внутри этих объектов одним из свойств является «жанры», представляющие собой массив строк (жанров).

Я пытаюсь найти или создать эти жанры в MongoDB. Затем добавьте эти идентификаторы объектов в массив и передайте этот массив при создании объектов Artist.

Но мои исполнители создаются в БД до того, как будет завершен процесс поиска или создания для их жанров.

Таким образом, строка fillRest запускается перед fillGenres.

Я пытался перейти с Object.values.forEach на for..of, for..in, различные асинхронные операции, обещания, модулирующий код ...

В основном добавлено, жду везде, где смогу (большинство в неправильных местах, вероятно)

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, так как он не возвращал обещание. Я использовал этот пакет для мангуста, у которого есть Обещания (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 ]

3 голосов
/ 22 мая 2019

Я раньше не использовал API Spotify, поэтому не могу сказать об этом много, но есть пара проблем, которые я вижу на первый взгляд.Сначала вы проверяете 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 голос
/ 22 мая 2019

Я ничего не знаю об API Spotify, так что это всего лишь предположение. В fillGenres у вас есть:

await Genre.findOrCreate({ name: element }, (err, result) => {
  genreIDs.push(result._id);
});

Вы передаете функцию обратного вызова. Иногда библиотеки позволяют вам использовать обратные вызовы или . Если вы передадите обратный вызов, он не вернет обещание. Поэтому я предполагаю, что цикл запускает все вызовы Genre.findOrCreate, что не возвращает обещание, поскольку вы используете обратный вызов. а затем сразу же возвращается. Затем вызывается 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 не возвращает Обещание, вы можете создать версию, которая будет:

function genreFindOrCreate(data) {
  return new Promise((resolve, reject) => {
    Genre.findOrCreate(data, (err, result) => {
      if(err) reject(err)
      else resolve(result)
    })
  })
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...