skip to Main Content

I try rewrite it to Promise only style, but i got an empty result.
How it’s right way for rewrite it only use a Promise, without async/await

async getWishes() {
    const wishes = await this.app.wishesService.getAllWishes()
    for (const wish of wishes) {
        const user = await this.app.userService.getUser(wish.userId)
        wish.userName = user ? user.name : 'anonimus'
        wish.userStars = user ? await this.app.favoriteService.getUserFavorites(user) : 0
    }
    return wishes
}

2

Answers


  1. The for loop is the hard part. The easiest way to do it would be to push all the body of the for loop into an array and then do a Promise.all():

    class SomeClass {
    
      getWishes() {
        return this.app.wishesService.getAllWishes().then(wishes => {
          let promises = [];
    
          for (const wish of wishes) {
            promises.push(
              this.app.userService.getUser(wish.userId).then(user => {
                if (user) {
                  return Promise.all([
                    user.name,
                    this.app.favoriteService.getUserFavorites(user)
                  ])
                }
                else {
                  return ['anonimus',0];
                }
              })
              .then(data => {
                 wish.userName = data[0];
                 wish.userStars = data[1];
              })
            )
          }
    
          return Promise.all(promises).then(() => {
            // Only need this to wait for all the promises we spawned
            // in the for loop above. Only return wishes after all of
            // them are done. Therefore we need this .then()
    
            return wishes;
          })
        })
      }
    
    }
    

    This is probably the simplest way to do it but it is not the same logic. In your original code the body of the for loop is waited for sequentially. That means there is only one query of userService and favoriteService going on for each iteration of the loop. In the code above we do it all in parallel. That means that all the queries are sent before any response form userService or favoriteService are received.

    To do it with exactly the same logic as your original code becomes more complicated because we cannot use a for loop. Instead we need to do some pseudo recursion ("pseudo: because we’re not really adding to the call stack, we’re replacing the function call with each loop):

    class SomeClass {
    
      wishesLoop(wishes) {
        const wish = wishes.pop();
    
        return this.app.userService.getUser(wish.userId).then(user => {
          if (user) {
            return Promise.all([
              user.name,
              this.app.favoriteService.getUserFavorites(user)
            ])
          }
          else {
            return ['anonimus',0];
          }
        })
        .then(data => {
           wish.userName = data[0];
           wish.userStars = data[1];
    
           if (wishes.length > 0) {
             return [... wishesLoop(wishes), wish];
           }
           else {
             return [wish];
           }
        })
      }
    
      getWishes() {
        return this.app.wishesService.getAllWishes().then(wishes => {
          return this.wishesLoop(wishes);
        })
      }
    
    }
    

    This was what we had to do before async/await. It’s still possible to handle any level of complexity with just callbacks (eg. using the .then() method of a Promise) however as you can see it’s much simpler to do it with async/await.

    Login or Signup to reply.
  2. This following version ensures that all promises are resolved according to the order of the array (aka sequentially), which can otherwise have unexpected results since your code has side effects.

    getWishes() {
      return this.app.wishesService.getAllWishes().then((wishes) => {
        let promiseChain = Promise.resolve(); // initialize head
    
        for (const wish of wishes) {
          promiseChain = promiseChain.then(() =>
            this.app.userService.getUser(wish.userId).then((user) => {
              wish.userName = user ? user.name : "anonimus";
              if (user) {
                return this.app.favoriteService
                  .getUserFavorites(user).then((favorites) => {
                    wish.userStars = favorites;
                  });
              } else {
                wish.userStars = 0;
              }
            })
          );
        }
    
        return promiseChain.then(() => wishes);
      });
    }
    

    Though as you can probably tell, this code is a lot harder to read and reason about. So outside of educational purposes, stick mostly to using async/await 🙂.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search