【问题标题】:Promises are not behaving as I expect them toPromise 没有像我期望的那样表现
【发布时间】:2016-10-26 00:25:28
【问题描述】:

我使用 Express 进行路由,使用 Sequelize 进行数据库管理。

app.get('/api/users/:username', (req, res) => {
  let username = req.params.username;
  findChattersPerRole()
    .then(chattersPerRole => {
      console.log('instakbot should\'ve been added by now...');
    });
});

函数 findChattersPerRole 将每个用户的用户名和角色作为另一个对象返回一个对象。

const findChattersPerRole = () => {
  return fetch('https://tmi.twitch.tv/group/user/instak/chatters')
    .then(parseJSON)
    .then(r => {
      let chatters = r.chatters;
      let chattersPerRole = Object.keys(chatters).map(role => {
        return chatters[role].map(username => {
          console.log('findOrCreateViewer will be executed after this');
          findOrCreateViewer(username, role);
          return {
            username: username,
            role: role
          };
        });
      });
      return Promise.resolve(flattenDeep(chattersPerRole));
    }).catch(err => {
      console.log(`Error in fetch: ${err}`);
    });
};

问题是,在我的路由中,我希望console.log('instakbot should\'ve been added by now...'); 在我的查看器插入数据库后执行,因为在我的函数 findChattersPerRole 中,我已经使用函数 findOrCreateViewer 插入了它们。我希望这会发生,因为在我的路线中,当 findChattersPerRole() 解决时,我会编写 console.log...

const findOrCreateViewer = (username, role) => {

  return Viewer.findOrCreate({
    where: {
      username
    },
    defaults: {
      instakluiten: 5,
      role
    }
  }).spread((unit, created) => {
    console.log('unit is: ', unit.dataValues.username);
    if(created){
      return `created is ${created}`;
    }else{
      return unit;
    }
  });

};

但是,在我的终端中,您可以看到事情并非如此......为什么我的承诺没有在预期的时间执行? Screenshot of my terminal

【问题讨论】:

  • findOrCreateViewer 似乎是异步的,因此您希望该函数返回一个承诺并执行类似return Promise.all(chattersPerRole); 之类的操作,其中chattersPerRole 是一组承诺(由return findOrCreateViewer(username, role).then(() => ({username, role})); 从@ 创建987654330@回调)。
  • 所以我可能会说 .then(return Promise.resolve(unit));?
  • 我不知道.spread 做了什么。它会返回一个承诺吗?如果是,那么您可能不需要更改该函数,但您必须对返回值做一些事情(如我的第一条评论所示)。目前你没有用它做任何事情:findOrCreateViewer(username, role);.
  • 是的价差返回一个承诺。哦,你在第一次编辑后就失去了我,我在这个问题上工作了很长时间,我的头很痛。为什么在这种情况下只调用 findOrCreateViewer(...) 是一件坏事,或者至少不是最好的决定?
  • 因为你没有对返回的承诺做任何事情,所以在你继续做其他事情之前,你不知道它何时“完成”(即何时插入数据)。如果你依赖异步操作按顺序运行,你需要等待它们中的每一个都完成,这就是 Promise 给你的。

标签: javascript ecmascript-6 sequelize.js es6-promise


【解决方案1】:

findOrCreateViewer(username, role); 之后的return {username: ...} 发生在函数被调用之后并且之前任何数据被插入之后。这也意味着return Promise.resolve(flattenDeep(chattersPerRole)); 在插入任何数据之前发生,等等。

你说findOrCreateViewer返回了一个promise,所以你需要等到这个promise被解决(即等到数据插入之后)才能继续做其他事情。

您希望 chattersPerRole 成为一组(数组)承诺,并且仅在所有承诺解决后继续。

Promise.all 很容易做到这一点:

const findChattersPerRole = () => {
  return fetch('https://tmi.twitch.tv/group/user/instak/chatters')
    .then(parseJSON)
    .then(r => {
      let chatters = r.chatters;
      let chattersPerRole = Object.keys(chatters).map(
        role => chatters[role].map(username => {
          console.log('findOrCreateViewer will be executed after this');
          return findOrCreateViewer(username, role).then(
            () => ({username, role})
          );
        });
      );
      return Promise.all(flattenDeep(chattersPerRole));
    }).catch(err => {
      console.log(`Error in fetch: ${err}`);
    });
};

现在findChattersPerRole 返回的promise 将在findOrCreateViewer 返回的所有promise 都解决后 解决。

【讨论】:

  • 感谢您花时间向我解释这一点,非常感谢。您能否解释一下 Promise.resolve 和 Promise.all 之间的区别?我现在了解 .all 的作用,但我认为我已经使用 .resolve 做到了这一点,但显然不是。另外,() => ({username, role}) 是等同于 return {...} 的 ES6 语法吗?我认为是,但我宁愿确定。再次感谢您的解释。
  • Promise.resolve() 实际上只是 new Promise(resolve => resolve(42)) 的简写,即它将一个值包装到一个 Promise 中并立即解析它。
【解决方案2】:

承诺没有魔法。返回一个promise并不意味着调用函数会阻塞,而是你可以轻松地链接回调来对结果做一些事情。您需要使用

function findChattersPerRole() {
  return fetch('https://tmi.twitch.tv/group/user/instak/chatters')
    .then(parseJSON)
    .then(r => {
      let chatters = r.chatters;
      let chattersPerRole = Object.keys(chatters).map(role => {
        return chatters[role].map(username => {
          console.log('findOrCreateViewer will be executed after this');
          return findOrCreateViewer(username, role).then(() => {
//        ^^^^^^                                   ^^^^^
            return {
              username: username,
              role: role
            };
          });
        });
      });
      return Promise.all(flattenDeep(chattersPerRole));
//                   ^^^ get a promise for an array of results from an array of promises
    }).catch(err => {
      console.log(`Error in fetch: ${err}`);
    });
}

【讨论】:

  • 感谢您的回答,我真的很喜欢您通过在我的代码中突出显示我的错误并添加评论来指出我的错误的方式。
猜你喜欢
  • 1970-01-01
  • 2016-12-03
  • 2019-05-08
  • 1970-01-01
  • 2013-09-26
  • 2019-12-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多