Home > Enterprise >  filtering a promise iterated result array into a new array vue js
filtering a promise iterated result array into a new array vue js

Time:01-29

How can I insert into a new arr promise values of an old array. A splice method required due to vue reactivity. Somehow the newArray is empty and not getting the any values. https://codesandbox.io/s/sweet-sammet-fyomv0?file=/src/components/Example2.vue

<template>
  <div >
    <h1>Example 2</h1>
    <input @click="send" type="button" value="Send" />
    <div  v-if="successIds.length">{{ successIds }}</div>
  </div>
</template>

<script>
/*
@return
  resolve: { id: 1, success: true }
  or
  reject: { success: false }
*/
const fakeApiRequest = (id) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = id % 2;
      success ? resolve({ id, success }) : reject({ success });
    }, 2000);
  });
};

export default {
  data() {
    return {
      // Fetch ids
      ids: [1, 2, 3, 4, 5, 6],
      // Complete ids
      successIds: [],
    };
  },

  methods: {
    async send() {
      let newArr = await Promise.allSettled(
        this.ids
          .map(fakeApiRequest)
          .filter((promise) => promise.status === "fulfilled") 
          .map((promise) => promise.value)
      );
      // console.log(newArr)   -->  gets an empty array
      this.successIds.splice(0, 0, newArr);
    },
  },
};
</script>

<style scoped>
.out {
  margin: 20px 0;
  color: #41b883;
}
</style>

CodePudding user response:

You can only apply filters on the awaited results of Promise.allSettled(). In other words, you have to place the .filter() inside it's then:

Working example:

const { createApp, defineComponent } = Vue;

const fakeApiRequest = (id) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = id % 2;
      success ? resolve({ id, success }) : reject({ success });
    }, 2000);
  });
};
const Test = defineComponent({
  template: "#test",
  data() {
    return {
      ids: [1, 2, 3, 4, 5, 6],
      successIds: [],
    };
  },
  methods: {
    async send() {
      this.successIds = await Promise.allSettled(this.ids.map(fakeApiRequest)).then(
        (results) =>
          results
            .filter((promise) => promise.status === "fulfilled")
            .map((promise) => promise.value)
      );
    },
  },
});
createApp({
  components: { Test },
}).mount("#app");
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<div id="app">
  <test />
</div>
<template id="test">
  <div >
    <h1>Example 2</h1>
    <input @click="send" type="button" value="Send" />
    <div  v-if="successIds.length">{{ successIds }}</div>
  </div>
</template>

Side note: you don't need to splice the results to keep reactivity. If you lose reactivity, you should ask a separate question about it, providing enough detail. As you can see, it works perfectly fine in my example.

CodePudding user response:

Several issues:

  • You are applying a .filter call on the promises returned by the .map call, but promises don't have a status property to filter by. You can only filter on the resolved values, not on the promises. So you first need to await the resolution of the allSettled promise before you can start filtering.

  • splice expects the insert-values as individual arguments, not as an array. So use the spread syntax

  • As you want to populate your successIds with id values, and your api returns objects with id and success properties, you still need to extract that id property from each object.

Here is a correction (I removed the vue dependecies):

const fakeApiRequest = (id) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = id % 2;
      success ? resolve({ id, success}) : reject({ success });
    }, 200);
  });
};

const app = {
  data() {
    return {
      // Fetch ids
      ids: [1, 2, 3, 4, 5, 6],
      // Complete ids
      successIds: [],
    };
  },
  methods: {
    async send() {
      const results = await Promise.allSettled(
        this.ids.map(fakeApiRequest)
      );
      const ids = results.filter((settled) => settled.status === "fulfilled")
                         .map((settled) => settled.value.id);
      this.successIds.splice(0, 0, ...ids);
      console.log(this.successIds); // for this demo only
    },
  },
};

// Mimic vue
app.methods.send.call(app.data());

CodePudding user response:

You need to filter the promises after they have been resolved:

const promises = this.ids.map(fakeApiRequest)
const settledPromises = await Promise.allSettled(promises);
const values = settledPromises.filter(p => p.status === "fulfilled").map(p => p.value)
this.successIds = values.concat(this.successIds)
  • Related