1

I have a computed property inside a Vue component that looks like so:

allUsers() {
  const vm = this;
  const users = this.getAll.map((item) => {
    let newUser = {};
    if (typeof item !== 'object') {
      newUser = {
        id: vm.userModel.id,
        userId: vm.userModel.userId,
        data: null,
        tenantData: null,
      };
    } else {
      newUser = item;
    }
    return newUser;
  });
  return users;
},

I need to insert some additional data into each newUser object, but getting that data requires 1) looping through another set of data for each newUser item and 2) getting the data returned from an axios call to a REST API endpoint:

async delete({ device, personId }) {
    return await super.perform(axios.delete(ServiceUrlProvider.gmiUrl()
      .concat('/person/')
      .concat(personId)
      .concat('/device/')
      .concat(device.deviceId)));
  }

Ideally I would be able to do something like this:

allUsers() {
  const vm = this;
  const users = this.getAll.map((item) => {
    let newUser = {};
    if (typeof item !== 'object') {
      newUser = {
        id: vm.userModel.id,
        userId: vm.userModel.userId,
        data: null,
        tenantData: null,
      };
    } else {
      newUser = item;
    }
    this.tenantApps.forEach((app) => {
      userDeviceService.fetchPersonAppDevice({
        id: item.id,
        appCode: app.code,
      })
        .then((resp) => {
          // Code here to add returned value to newUser object.
        });
    });

    return newUser;
  });
  return users;
},

but since it is bad practice to allow async actions in computed properties, I have to try something else. Based on what I found, I'm trying vue-async-computed, and I've moved my method to the separate asyncComputed object:

  asyncComputed: {
    allUsers() {
      const vm = this;
      const users = this.getAll.map((item) => {
        let newUser = {};
        if (typeof item !== 'object') {
          newUser = {
            id: vm.userModel.id,
            userId: vm.userModel.userId,
            data: null,
            tenantData: null,
          };
        } else {
          newUser = item;
        }

        this.tenantApps.forEach((app) => {
          userDeviceService.fetchPersonAppDevice({
            id: item.id,
            appCode: app.code,
          })
            .then((resp) => {
              if (Array.isArray(resp.data) && resp.data.length > 0) {
                newUser.hasDevice = true;
              } else {
                newUser.hasDevice = false;
              }
            });
        });

        return newUser;
      });
      return users;
    },
  },

My problem is getting allUsers() to wait for the returned call to userDeviceService.fetchPersonAppDevice(), since as it is now, it finishes and returns without waiting. I can't just use await on the forEach loop. How do I need to implement that call so that I can add the returned data to my newUser object?

UPDATE: Per comment by Estus Flask below, I've modified my allUsers computed value like so:

asyncComputed: {
  async allUsers() {
    const vm = this;
    const users = this.getAll.map((item) => {
      let newUser = {};
      if (typeof item !== 'object') {
        newUser = {
          id: vm.userModel.id,
          userId: vm.userModel.userId,
          data: null,
          tenantData: null,
        };
      } else {
        newUser = item;
      }
      const devicePromises = [];
        this.tenantApps.forEach((app) => {
          devicePromises.push(userDeviceService.fetchPersonAppDevice({
            id: item.id,
            appCode: app.code,
          }));
        });
        const devices = await Promise.all(devicePromises);
      return newUser;
    });
    return users;
  },
},

However, I get an error on the await Promise.all(devicePromises); call saying that the await operator can only be used in an async function. I've changed allUsers to be asynchronous, so why the error?

wonder95
  • 3,825
  • 8
  • 45
  • 74
  • As I understand, the point of vue-async-computed is that it can chain a promise that a function returns. If it doesn't, there's nothing to wait and nothing to compute from. *I can't just use await on the forEach loop* - this is a problem to start with. Don't use forEach, https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop . Make allUsers a proper `async` function that returns only when all asynchronous processes inside of it are completed. – Estus Flask Apr 17 '20 at 10:47
  • Thanks, @EstusFlask. Any chance you could give me a code sample in an answer? I can give you credit that way, too. – wonder95 Apr 17 '20 at 14:59
  • @EstusFlask, you say ` Make allUsers a proper async function`, but since computed properties can't be asynchronous, do you mean just have `allUsers()` be a separate function called by the computed property? – wonder95 Apr 17 '20 at 16:08
  • It should be a proper `async` function in order to be used with vue-async-computed, i.e. return a promise of entire asynchronous operation. I'd suggest to check the question I linked above and try to rewrite it. `allUsers` should be made `async`, `forEach` should be replaced with `for..of` or `Promise.all`+`map`, `then` can be replaced with `await` because this is what async/await is for. – Estus Flask Apr 17 '20 at 16:20

0 Answers0