简体   繁体   English

代码在里面。然后在Promise之前执行

[英]Code inside .then executing before the Promise

I have a JSON file from Spotify with objects that represent artists. 我有来自Spotify的JSON文件,其中包含代表艺术家的对象。 Inside of those objects, one of the properties is 'genres' that is an array of strings (genres). 在这些对象中,其中一个属性是'genres',它是一个字符串数组(流派)。

What I'm trying to do is find or create those genres in MongoDB. 我想要做的是在MongoDB中查找或创建这些类型。 Then add those object IDs into an array and pass that array when I create my Artist objects. 然后将这些对象ID添加到数组中,并在创建Artist对象时传递该数组。

But, my Artists get created in the DB way before that find or create process for their genres is done. 但是,我的艺术家在找到或创建他们的流派的过程之前就以DB方式创建了。

Thus the line fillRest runs before fillGenres. 因此,行fillRest在fillGenres之前运行。

I've tried changing from Object.values.forEach to for..of, for..in, different asyncs, promises, modulating code... 我试过从Object.values.forEach更改为for..of,for..in,不同的asyncs,promises,调制代码......

Basically added await everywhere I can (most in the wrong places probably) 基本上添加等待我可以到处(大多数在错误的地方)

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();

Artists get added to MongoDB with genres set as []. 艺术家被添加到MongoDB,类型设置为[]。 After that I get console output with good genreID arrays (that were supposed to be inside there^ instead of genres). 之后,我得到了具有良好genreID数组的控制台输出(应该在那里^而不是类型)。

Resolved - EDIT 已解决 - 编辑

Thanks to everyone who helped. 感谢所有帮助过的人。 The problem was in findOrCreate as it did not return a promise. 问题出在findOrCreate中,因为它没有返回一个promise。 I used this package for mongoose instead that has Promises ( https://www.npmjs.com/package/mongoose-findorcreate ). 我使用这个包用于mongoose而不是Promises( https://www.npmjs.com/package/mongoose-findorcreate )。

And the code now is 现在的代码是

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);
        });
    }
  }

and in SpotifySeed 在SpotifySeed中

const genreIDs = await fillGenres(entry.Genres);
      await fillRest(entry, genreIDs);

I haven't used the Spotify APIs before, so I can't say much about that but there are a couple of issues I see at first glance. 我之前没有使用过Spotify API,所以我不能说太多,但我乍一看有几个问题。 First, you're checking if (array !== 'undefined') { , which is checking if the array variable is a string that is literally 'undefined' (not the value undefined ). 首先,你要检查if (array !== 'undefined') { ,这是检查array变量是否是一个字面上'undefined'的字符串(不是undefined的值)。 I'm fairly certain that is not what you intended. 我很确定这不是你想要的。 You would be better off using Array.isArray(array) here, if you're wanting to make sure array is actually an Array. 如果你想确保array实际上是一个数组,你最好在这里使用Array.isArray(array)

Second, you're using async functions and Promises mixed together, which (imo), you generally shouldn't do. 其次,你正在使用异步函数和Promise混合在一起,这些(imo),你通常不应该这样做。 You should use one or the other, so it's consistent and easier to follow. 您应该使用其中一个,因此它一致且易于遵循。 If you use await instead of .then , you will be able to write it in a more "synchronous" fashion and it should be easier to understand. 如果你使用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();

I don't know anything about the Spotify API, so this is just a guess. 我对Spotify API一无所知,所以这只是猜测。 In fillGenres , you have: fillGenres ,您有:

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

You're passing a callback function. 你正在传递一个回调函数。 Sometimes libraries will allow you to use promises or callbacks. 有时库会允许您使用promises callbacks。 If you pass a callback, it won't return a promise. 如果您传递回调,它将不会返回承诺。 So I'm guessing that the loop starts off all the calls to Genre.findOrCreate , which doesn't return a promise since you're using a callback. 所以我猜测循环开始对Genre.findOrCreate所有调用,因为你正在使用回调,它不会返回一个承诺。 and then immediately returns. 然后立即返回。 Then fillRest is called and may finish before all the Genre.findOrCreate calls. 然后fillRest并在所有Genre.findOrCreate调用之前完成。

You want something like: 你想要的东西:

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

Though even better would be this: 虽然更好的是:

function fillGenres(genreNames) {
  if(!genreNames || !genreNames.length) return Promise.resolve([])

  return Promise.all(genreNames.map(name => {
    return Genre.findOrCreate({ name })
      .then(result => result._id)
  })
}

This will run all the genre calls at the same time and return them all when they're done, rather than waiting to add one after another (as in your for loop). 这将同时运行所有类型调用并在完成后将它们全部返回,而不是等待一个接一个地添加(如在for循环中)。

If Genre.findOrCreate does not return a Promise, you could create a version that does: 如果Genre.findOrCreate没有返回Promise,您可以创建一个版本:

function genreFindOrCreate(data) {
  return new Promise((resolve, reject) => {
    Genre.findOrCreate(data, (err, result) => {
      if(err) reject(err)
      else resolve(result)
    })
  })
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM