Async/Await操作发生在预期的顺序之外

man*_*eko 1 javascript asynchronous node.js express

我正在尝试更新数据库中的一堆记录,基于业余爱好网站的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
Run Code Online (Sandbox Code Playgroud)

请注意,它在输出"All done"后更新记录,并从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
Run Code Online (Sandbox Code Playgroud)

我做错了什么,在处理任何记录之前让它说"全部完成"?这是相关的代码:

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));
});
Run Code Online (Sandbox Code Playgroud)

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

jfr*_*d00 5

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

因此,如果您希望使用它来对您的操作进行排序,那么它就不会这样做.此外,因为你没有从你的.map()回调中返回任何东西,所以诺言updateMovieDetails(topMovie)也没有回复,Promise.all()所以它不会等待它们.

改变这个:

await Promise.all(topMovies.map(async(topMovie) => {
  await updateMovieDetails(topMovie);
}));
Run Code Online (Sandbox Code Playgroud)

对此:

await Promise.all(topMovies.map((topMovie) => {
  return updateMovieDetails(topMovie);
}));
Run Code Online (Sandbox Code Playgroud)

然后,Promise.all()意志等待所有updateMovieDetails()呼叫完成.

此外,updateMovieDetails()不会返回与其活动相关联的承诺,因此Promise.all()对这些承诺没有做任何事情.

您可以手动默认该imbdb()函数,然后从中返回生成的promise:

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();
      }
    });
};
Run Code Online (Sandbox Code Playgroud)

这是我推荐的积累:

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));
});
Run Code Online (Sandbox Code Playgroud)