首页 > 解决方案 > 功能代码未按正确顺序执行,异步等待执行错误

问题描述

我在端点中尝试做的是:

  1. 进行 API 调用,返回 JSON
  2. 对于每个项目:在我们的数据库中搜索它
  3. 如果找到,请跳过它。
  4. 如果未找到,将其推送到数组“响应”中

这是我的代码:

app.get("/test", (req,res) => {

  spotifyApi.getUserPlaylists({ limit: 50 })
  .then(function(data) {
    let finalres = [];
    const tbp = data.body.items;
    // res.send('ok stop loading');
    
    tbp.forEach(element => locateit(element,finalres));

    console.log('This is the length of finalres, which should be 1:', finalres.length);
    finalres.forEach(item =>{console.log(item)});

    function locateit(item, finalres){
      const thisplaylistid = item.id;

      collection.find({ "id" : thisplaylistid }).toArray((error, result) => {
        if(error) {
          return res.status(500).send(error);
        }

        if(result.length == 0) {    // if we don't find this playlist in our DB
          console.log('This playlist is not in our database: ');
          console.log(thisplaylistid);
          finalres.push(thisplaylistid);
        }
        else{  //if it's already in our DB
          console.log('This item is in our database.'); //This should be printed first, six times.
        }
      });
    };
  });
});

的内容data.body.items是 7 项,其中只有前 6 项在我们的数据库中。这意味着,最后一项应该被推入finalres。因此,预期的控制台结果应该是:

This item is in our database.
This item is in our database.
This item is in our database.
This item is in our database.
This item is in our database.
This playlist is not in our database: 
3uDLmuYPeRUxXouxuTsWOe
This is the length of finalres, which should be 1: 1
3uDLmuYPeRUxXouxuTsWOe

但相反,我得到了这个:

This is the length of finalres, which should be 1: 0
This should be displayed first, six times.
This should be displayed first, six times.
This should be displayed first, six times.
This should be displayed first, six times.
This should be displayed first, six times.
This should be displayed first, six times.
This playlist is not in our database: 
3uDLmuYPeRUxXouxuTsWOe

它显然没有按正确的顺序执行。我尝试使用异步等待,但我很难理解应该在哪里/如何实现它。有什么帮助吗?这是我尝试过的部分,但我得到了与以前相同的控制台结果:

async function locateit(item, finalres){
      const thisplaylistid = item.id;

      await collection.find({ "id" : thisplaylistid }).toArray((error, result) => {
...

更新

在阅读了有关 async-wait 和 promise 的更多信息后,我尝试这样做,但我仍然得到相同的输出。

app.get("/test", (req,res) => {

  spotifyApi.getUserPlaylists({ limit: 50 })
  .then(function(data) {
    let finalres = [];
    const tbp = data.body.items;
    // res.send('ok stop loading');
    
    for (const playlist of tbp) {
      async function doWork() {
        const found = await indb(playlist.id); //returns t/f if found or not found
        if (!found){
          finalres.push(playlist);
        }
      }
      doWork();
    }
    console.log('This is the length of finalres and it should be 1: ',finalres.length);
  })
});

indb 函数如下所示:

function indb(thisplaylistid){
  return new Promise((resolve, reject) =>{
      console.log('Searching in our DB...');
      collection.find({ "id" : thisplaylistid }).toArray((error, result) => {
          if(result.length == 0) {    // if we don't find this playlist in our DB
              console.log('This playlist is not in our database: ');
              console.log(thisplaylistid);
              resolve(false); //returns the id
          }
          else{  //if it's already in our DB
              console.log('This item is in our database.'); //This should be printed first, six times.
              resolve(true);
          }
      });
  })
}

标签: javascriptnode.jsasync-await

解决方案


这里的问题是,无论您是否有异步承诺在其中运行,forEach 解析总是解析为 void。

因此,您的代码将在执行 forEach 中的语句之前返回

正确的做法是使用#Promise.all等待所有承诺解决

试试这个:


按照 Bergi 的建议更新了使用 promise 而不是回调(更可取)

app.get("/test", (req, res) => {

  spotifyApi.getUserPlaylists({ limit: 50 })
    .then((data) => {
      // :refac: more meaningful variable names
      const playlists = data.body.items
      return Promise.all(
        playlists.map(
          // :refac: destructuring to get only the id, other ain't necessary
          async({ id }) => 
              collection.find({ id }).toArray()  
        )
      )
      .then(playlistsById => 
        // :refac: no error occurred fetching playlists
        const nonEmptyPlaylists = playlistsById.filter(playlistById => playlistById.length !== 0)
        res.status(200).send(nonEmptyPlaylists)
      )
      .catch(error => {
        // :refac: some error occurred at searching some playlist
        console.log('error', error) 
        // :refac: if you might expect that is going to throw an error here, the code shouldn't be 500
        return res.status(400).send(error)
      })
    })
})

推荐阅读