3

I have two forms with data that needs to be saved using two separate post APIs. I am using Promise.all() to save all the data from these two forms at once and its working as it should. The code is as follows:

saveAll() {
        Promise.all([
            this.$axios.post('v1/dummy-api/one',
              this.saveFormOne()
            ),
            this.$axios.post('v1/dummy-api/two',
              this.saveFormTwo()
            )
          ])
          .then(() => {
            this.success = 'Added Successfully.';
          })
          .catch(error => {
            this.error = 'Error.';
          })
      },

The issue am trying to solve is I need to check if any of those two request fails and if it does then prevent/cancel the saving of the other one (which succeed) until all of them succeed. I've tried try/catch with no luck.

Any help will greatly appreciated!

gxvr
  • 296
  • 2
  • 17
  • "how can I prevent the other to be saved" - what do you mean by "saved"? "Until the other one succeed" - I get now, I think you meant "Unless the other succeed" :) – Sergiy Ostrovsky May 19 '22 at 12:20
  • 1
    Hello @SergiyOstrovsky I've edited the question please check – gxvr May 19 '22 at 12:31
  • Did you gave a look to [Promise.allSettled()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled)? Will give you the the state of every Promise and if they are properly fulfilled or not. – kissu May 19 '22 at 13:26

4 Answers4

4

You need to use Axios AbortController (https://axios-http.com/docs/cancellation). Be wary though that cancelling a request might not necessarily guarantee that the POST has not already been properly executed. If you must not call the other endpoint if one fails, the easiest way would be to chain them and call one after another.

Anyway, a way to achieve this is shown underneath. Note that I needed to modify it a bit in order for it to be executable inside stackoverflow answer. You'd get a general idea from it, though.

function saveAll() {
    const cancel = new AbortController();
    Promise.all([
            axios.get('https://test-stackoverflow.free.beeceptor.com', {}, {
                signal: cancel.signal
            }).catch((e) => {
                cancel.abort();
                throw e;
            }),
            axios.get('https://thiswillfail.com/fail', {}, {
                signal: cancel.signal
            }).catch((e) => {
                console.log('second request has failed')
                cancel.abort();
                throw e;
            })
        ])
        .then(() => {
            alert('Added Successfully.');
        })
        .catch(error => {
            alert('Error.');
        })
}
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

<button onclick="saveAll()">save all</button>
karoluS
  • 2,980
  • 2
  • 23
  • 44
  • Thanks! This one does work and alerts if one fails but the data is posted before pending/cancellation of the requests. – gxvr May 19 '22 at 12:14
  • Take a look at @kissu answer for the reason for it. Cancelling a request might not be enough, because changes on the backend might have already been made. – karoluS May 23 '22 at 07:08
2
async saveAll() { 
 const resp1 = await this.$axios.post('v1/dummy-api/one', this.saveFormOne())
 const resp2 = await this.$axios.post('v1/dummy-api/two', this.saveFormTwo())
}

I don't know if this can be solved

王超华
  • 402
  • 3
  • 3
  • This doesn't work. It will just send two request and no way to prevent the post of all if one of them fails. – gxvr May 19 '22 at 11:50
2

Promise.all() is not what you need if you need to manage these API calls. You can do somethin similar to what 王超华 suggested, but with additional try/catch

async function saveAll() { 
  let first;
  try {
    first = await this.$axios.post('v1/dummy-api/one', this.saveFormOne())
  } catch (e) {
    console.log('first failed')
    return
  }
  console.log('first', first)

  const second = await this.$axios.post('v1/dummy-api/two', this.saveFormTwo())
  console.log('second', second)
}

Or if you want to use Promises

saveAll() {
      this.$axios.post('v1/dummy-api/one', this.saveFormOne())
      .then(() => this.$axios.post('v1/dummy-api/two', this.saveFormTwo())
      .then(() => this.success = 'Added Successfully.')
      .catch(error => this.error = 'Error.')
  }
Sergiy Ostrovsky
  • 2,372
  • 2
  • 16
  • 23
2

I'm not sure how you can send a request, but cancel it at the same time.
A request can fail at several points, if your thing is failing when the DB is trying to save the entity, you cannot really cancel it from the client-side (without sending another HTTP call).

Either send another HTTP call from your client to update the backend on the failed status or let the backend rollback what you've tried if it's not correct.

This question is asking how to solve an issue regarding the design of a system here. The solution is not on the frontend, but rather on the backend/implementation itself.

You need to figure out a way to:

  • communication between the actions done on separate backends (link them somehow via a middleware backend or alike)
  • make a validation on the client before sending the 2 HTTP calls (via a simulation or a client side validation)
  • put in place some rollbacks

The cleanest approach would be to use Promise.allSettled, with a param of something like a dry-run ala git regarding both endpoints (if available), that way you could simulate the execution and result of both (and prevent any network errors), then re-run the query on both side if the simulation was correct.

kissu
  • 40,416
  • 14
  • 65
  • 133