【问题标题】:Async/Await operations happening out of expected order异步/等待操作超出预期顺序
【发布时间】:2018-07-13 09:15:06
【问题描述】:

我正在尝试根据 IMDB 结果更新数据库中的一堆记录,以供一个爱好网站学习 Node 和 React。我对异步/等待代码和承诺也很陌生,所以我很挣扎。

根据我的阅读,这段代码应该:

  • 从我的数据库中检索我所有的“TopMovies”记录
  • 等待所有记录填充后再继续
  • 异步调用处理以从 IMDB 更新每条记录的一些基本字段
  • 将每条记录保存为异步线程的一部分
  • 返回所有已处理的记录

相反,我得到以下控制台信息:

[0] Executing (default): SELECT `id`, `rank`, `name`, `IMDBId`, `rating`, `genre`, `posterUrl`, `createdAt`, `updatedAt`, `reviewerId`, `yearId` FROM `TopMovies` AS `TopMovie`;
[0] Movie count: 150
[0] All done
[0] Updated movie: Chef
[0] Executing (default): UPDATE `TopMovies` SET `rating`=3.5,`genre`='Adventure',`posterUrl`='https://images-na.ssl-images-amazon.com/images/M/MV5BMTY5NTYzNTA1M15BMl5BanBnXkFtZTgwODIwODU1MTE@._V1_UY268_CR0,0,182,268_AL_.jpg',`updatedAt`='2018-02-02 19:49:17.036 +00:00' WHERE `id` = 44
[0] Updated movie: The Champions
[0] Executing (default): UPDATE `TopMovies` SET `rating`=4.5,`genre`='10 October 2015 (USA) ',`posterUrl`='https://images-na.ssl-images-amazon.com/images/M/MV5BMTk5MjM2MjQ4MF5BMl5BanBnXkFtZTgwNjIxMDA5NzE@._V1_UX182_CR0,0,182,268_AL_.jpg',`updatedAt`='2018-02-02 19:49:17.092 +00:00' WHERE `id` = 87
[0] Updated movie: Trance

请注意,它在输出“全部完成”后更新记录,并从 app.get() 函数返回。理想情况下,它会更像:

[0] Executing (default): SELECT `id`, `rank`, `name`, `IMDBId`, `rating`, `genre`, `posterUrl`, `createdAt`, `updatedAt`, `reviewerId`, `yearId` FROM `TopMovies` AS `TopMovie`;
[0] Movie count: 150
[0] Updated movie: Chef
[0] Executing (default): UPDATE `TopMovies` SET `rating`=3.5,`genre`='Adventure',`posterUrl`='https://images-na.ssl-images-amazon.com/images/M/MV5BMTY5NTYzNTA1M15BMl5BanBnXkFtZTgwODIwODU1MTE@._V1_UY268_CR0,0,182,268_AL_.jpg',`updatedAt`='2018-02-02 19:49:17.036 +00:00' WHERE `id` = 44
[0] Updated movie: The Champions
[0] Executing (default): UPDATE `TopMovies` SET `rating`=4.5,`genre`='10 October 2015 (USA) ',`posterUrl`='https://images-na.ssl-images-amazon.com/images/M/MV5BMTk5MjM2MjQ4MF5BMl5BanBnXkFtZTgwNjIxMDA5NzE@._V1_UX182_CR0,0,182,268_AL_.jpg',`updatedAt`='2018-02-02 19:49:17.092 +00:00' WHERE `id` = 87
[0] Updated movie: Trance
[0] All done
* Returns from app.get

我做错了什么,导致它在处理任何记录之前说“全部完成”?以下是相关代码:

const imdb = require('imdb');
const _require = require('./models/index'),
  Year = _require.Year,
  Reviewer = _require.Reviewer,
  TopMovie = _require.TopMovie,
  ViewStat = _require.ViewStat,
  Op = _require.Sequelize.Op;

app.get('/imdb_import', (req, res) => {
  const UpdateMovies = async () => {
    const topMovies = await TopMovie.findAll();
    console.log("Movie count: " + _.size(topMovies));

    // await topMovies.forEach(async topMovie => {
    //   await updateMovieDetails(topMovie);
    // });
    await Promise.all(topMovies.map(async(topMovie) => {
      await updateMovieDetails(topMovie);
    }));

    console.log("All done");
    return topMovies;
  };

  const updateMovieDetails = topMovie => {
    return imdb(topMovie.get('IMDBId'), (err, data) => {
      if (err) {
        console.log(err.stack);
      }
      if (data) {
        topMovie.posterUrl = data.poster;
        topMovie.rating = Math.round(data.rating) / 2;
        if (data.genre) {
          topMovie.genre = data.genre[0];
        }
        topMovie.save();
        console.log("Updated movie: " + topMovie.name);
      }
    });
  };

  UpdateMovies()
    .then((data) => res.status(200).json(data))
    .catch(error => console.log(error));
});

可以在此处的“async_imdb”分支中找到此项目的代码:https://github.com/mandreko/ov_stats/tree/async_imdb 如果有人想在本地运行它

【问题讨论】:

    标签: javascript node.js express asynchronous


    【解决方案1】:

    await updateMovieDetails(topMovie); 在这段代码中没有完成任何事情。请记住,await 不会阻止包含函数完成并返回一个承诺。它只影响函数内的代码。

    所以,如果您希望使用它来对您的操作进行排序,它不会那样做。此外,由于您没有从 .map() 回调中返回任何内容,因此来自 updateMovieDetails(topMovie) 的承诺不会返回到 Promise.all(),因此它不会等待它们。

    改变这个:

    await Promise.all(topMovies.map(async(topMovie) => {
      await updateMovieDetails(topMovie);
    }));
    

    到这里:

    await Promise.all(topMovies.map((topMovie) => {
      return updateMovieDetails(topMovie);
    }));
    

    然后,Promise.all() 将等待所有 updateMovieDetails() 调用完成。

    此外,updateMovieDetails() 不会返回与其活动相关联的承诺,因此对这些活动执行 Promise.all() 不会完成任何事情。

    您可以手动对imbdb() 函数进行承诺,然后像这样返回结果承诺:

    const util = require('util');
    // make promsified version of imdb()
    const imdbPromise = util.promisify(imdb);
    
    const updateMovieDetails = topMovie => {
        return imdbPromise(topMovie.get('IMDBId')).then(data => {
          if (data) {
            topMovie.posterUrl = data.poster;
            topMovie.rating = Math.round(data.rating) / 2;
            if (data.genre) {
              topMovie.genre = data.genre[0];
            }
            console.log("Updated movie: " + topMovie.name);
            topMovie.save();
          }
        });
    };
    

    这里是我推荐的东西的积累:

    const util = require('util');
    // make promsified version of imdb()
    const imdbPromise = util.promisify(imdb);
    
    app.get('/imdb_import', (req, res) => {
        const UpdateMovies = async () => {
          const topMovies = await TopMovie.findAll();
          console.log("Movie count: " + _.size(topMovies));
    
          await Promise.all(topMovies.map(updateMovieDetails);
    
          console.log("All done");
          return topMovies;
        };
    
        const updateMovieDetails = topMovie => {
            return imdbPromise(topMovie.get('IMDBId')).then(data => {
              if (data) {
                topMovie.posterUrl = data.poster;
                topMovie.rating = Math.round(data.rating) / 2;
                if (data.genre) {
                  topMovie.genre = data.genre[0];
                }
                console.log("Updated movie: " + topMovie.name);
                return topMovie.save();
              }
            });
        };
    
        UpdateMovies()
          .then((data) => res.status(200).json(data))
          .catch(error => console.log(error));
    });
    

    【讨论】:

    • 当我删除那个等待时,我仍然得到相同的乱序操作。
    • @mandreko - Promise.all() 是一个并行工具。它同时运行所有异步操作,没有保证执行顺序。如果要对它们进行排序,请在循环中使用for 循环和await。很难准确地说出您要达到的目标。关于订购,您的目标到底是什么?
    • Promise.all() 可以完全并行运行它们。它们不必按任何特定顺序进行更新。但是,我想要实现的是它从主 app.get() 主函数调用返回更新的记录。现在,它在电影计数和“全部完成”之后立即返回,然后遍历并更新所有记录。
    • @mandreko - 好吧,updateMovieDetails() 不会返回承诺,因此尝试在其上使用await 或将其结果发送到Promise.all() 不会做任何事情。您需要“承诺”该功能。这实际上是更大的失误。
    • 我怎样才能改变它以获得预期的结果?如果我让它返回 topMovie.save(),它也不会这样做。
    猜你喜欢
    • 1970-01-01
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 2019-04-19
    • 1970-01-01
    • 2020-03-04
    • 2019-04-07
    • 2023-03-03
    相关资源
    最近更新 更多