.then 中的代码在 Promise 之前执行

Posted

技术标签:

【中文标题】.then 中的代码在 Promise 之前执行【英文标题】:Code inside .then executing before the Promise 【发布时间】:2019-10-08 09:54:59 【问题描述】:

我有一个来自 Spotify 的 JSON 文件,其中包含代表艺术家的对象。在这些对象内部,其中一个属性是“流派”,它是一个字符串数组(流派)。

我想做的是在 MongoDB 中找到或创建这些类型。然后将这些对象 ID 添加到一个数组中,并在我创建 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);

【问题讨论】:

【参考方案1】:

我对 Spotify API 一无所知,所以这只是一个猜测。在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 不返回 Promise,您可以创建一个版本:

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

【讨论】:

谢谢!你让我走上了正确的轨道。修改它以使用 Promises。【参考方案2】:

我以前没有使用过 Spotify API,所以我不能说太多,但我乍一看有几个问题。首先,您要检查if (array !== 'undefined') ,即检查array 变量是否是一个字面意思为'undefined' 的字符串(而不是值undefined)。我相当肯定这不是你想要的。如果你想确保array 实际上是一个数组,你最好在这里使用Array.isArray(array)

其次,您将 async 函数和 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();

【讨论】:

Spotify 有时会返回流派:“未定义”(文字字符串)而不是空数组。很奇怪。 感谢您的回答!会去看看 啊,我明白了。这确实很奇怪!没问题,希望一切顺利! 太棒了!很高兴听到它。

以上是关于.then 中的代码在 Promise 之前执行的主要内容,如果未能解决你的问题,请参考以下文章

React Native 为啥我的代码在完成任务之前执行? Promise.all().then() 异步问题

Promise.all 的 then() 函数在 Promise 完成之前执行 - Ionic/Angular

为啥这个异步函数在它之前定义的等效 Promise.then 链之前执行?

.then() 在嵌套的 promise.all 和 fetch 完成之前执行

Promise then中回调为什么是异步执行?Promise执行机制问题

关于多个Promise对象及then()函数的执行顺序的研究记录