Search code examples
javascriptecmascript-6sequelize.jses6-promise

Promises are not behaving as I expect them to


I'm using Express for routing and Sequelize for DB management.

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

The function findChattersPerRole returns an object with each user's username and role as another object.

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}`);
    });
};

The problem is, in my route, I expect the console.log('instakbot should\'ve been added by now...'); to be executed AFTER my viewers got inserted into the database because in my function findChattersPerRole I already insert them with the function findOrCreateViewer. I expect this to happen because in my route I write the console.log when findChattersPerRole() is resolved...

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;
    }
  });

};

However, in my terminal you can see that this is not the way it's happening... Why aren't my promises being executed at the expected time? Screenshot of my terminal


Solution

  • The return {username: ...} after findOrCreateViewer(username, role); happens immediately after the function is called and before any data has been inserted. That also means that return Promise.resolve(flattenDeep(chattersPerRole)); happens before any data has been inserted, etc.

    You said findOrCreateViewer returns a promise, so you need to wait until that promise is resolved (i.e. wait until after the data was inserted) before continuing with something else.

    You want chattersPerRole to be an array of (arrays of) promises and only proceed after all the promises are resolved.

    This is easy to do with 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}`);
        });
    };
    

    Now the promise returned by findChattersPerRole will be resolved after all the promises returned by findOrCreateViewer are resolved.