Axios/Vue - Prevent axios.all() to keep executing Axios/Vue - Prevent axios.all() to keep executing vue.js vue.js

Axios/Vue - Prevent axios.all() to keep executing


EDIT: @tony19's answer is much better as it allows to cancel requests still pending after first error, and does not need any extra library.


One solution would be to assign a unique identifier (I will use the uuid/v4 package in this example, feel free to use something else) to all the requests you use at the same time:

import uuid from 'uuid/v4'const actions = {    fetchData({commit, dispatch}) {        const config = {            _uuid: uuid()        }        function getChannels() {            return http.get('channels', config)        }        function getContacts() {            return http.get('conversations', config)        }        function getEventActions() {            return http.get('events/actions', config)        }        // 20 more functions calls        axios.all([            getChannels(),            getContacts(),            getEventActions()        ]).then(axios.spread(function (channels, contacts, eventActions) {            dispatch('channels/setChannels', channels.data, {root: true})            dispatch('contacts/setContacts', contacts.data, {root: true})            dispatch('events/setActions', eventActions.data, {root: true})        }))    }}

Then, in your interceptor, you can choose to handle the error a single time using this unique identifier:

export default (http, store, router) => {    // Here, you create a variable that memorize all the uuid that have    // already been handled    const handledErrors = {}    http.interceptors.response.use(response => response, (error) => {        // Here, you check if you have already handled the error        if (error.config._uuid && handledErrors[error.config._uuid]) {            return Promise.reject(error)        }        // If the request contains a uuid, you tell         // the handledErrors variable that you handled        // this particular uuid        if (error.config._uuid) {            handledErrors[error.config._uuid] = true        }        // And then you continue on your normal behavior        const {response} = error;        let message = 'Ops. Algo de errado aconteceu...';        if([401].indexOf(response.status) > -1){            localforage.removeItem('token');            router.push({                name: 'login'            });            Vue.notify({                group: 'panel',                type: 'error',                duration: 5000,                text: response.data.message ? response.data.message : message            });        }        return Promise.reject(error);    })}

Additional note, you could simplify your fetchData function to this:

const actions = {    fetchData({commit, dispatch}) {        const config = {            _uuid: uuid()        }        const calls = [            'channels',            'conversations',            'events/actions'        ].map(call => http.get(call, config))        // 20 more functions calls        axios.all(calls).then(axios.spread(function (channels, contacts, eventActions) {            dispatch('channels/setChannels', channels.data, {root: true})            dispatch('contacts/setContacts', contacts.data, {root: true})            dispatch('events/setActions', eventActions.data, {root: true})        }))    }}


The upvoted answer proposes a solution that requires waiting for all responses to complete, a dependency on uuid, and some complexity in your interceptor. My solution avoids all that and addresses your goal of terminating Promise.all() execution.

Axios supports request cancelation, so you could wrap your GET requests with an error handler that cancels the other pending requests immediately:

fetchData({ dispatch }) {  const source = axios.CancelToken.source();  // wrapper for GET requests  function get(url) {    return axios.get(url, {        cancelToken: source.token // watch token for cancellation      }).catch(error => {        if (axios.isCancel(error)) {          console.warn(`canceled ${url}, error: ${error.message}`)        } else {          source.cancel(error.message) // mark cancellation for all token watchers        }      })  }  function getChannels() {    return get('https://reqres.in/api/users?page=1&delay=30'); // delayed 30 secs  }  function getContacts() {    return get('https://reqres.in/api/users?page=2'); // no delay  }  function getEventActions() {    return get('https://httpbin.org/status/401'); // 401 - auth error  }  ...}

In your interceptor, you'd also ignore errors from request cancellations:

export default (http, store, router) => {  http.interceptors.response.use(    response => response,    error => {      if (http.isCancel(error)) {        return Promise.reject(error)      }      ...      // show notification here    }}

demo


As an alternative to Axios cancel, you can use Bluebird Promise Cancellation which is simpler.

The advantages of the new cancellation compared to the old cancellation are:

  • .cancel() is synchronous.
  • no setup code required to make cancellation work
  • composes with other bluebird features, like Promise.all

Here is a demo. I've added some logging in axios.get(...).then(...) to track if each call completes.

Comment out the line promises.forEach(p => p.cancel()) to verify that without cancellation the non-erroring calls will run to completion.

//for demo, check if fetch completes const logCompleted = (res) => console.log(`Promise completed, '${res.config.url}'`) function getChannels() {  return axios.get("https://reqres.in/api/users?page=1&delay=5").then(logCompleted)}function getContacts() {  return axios.get("https://reqres.in/api/users?page=2").then(logCompleted)}function getEventActions() {  return axios.get("https://httpbin.org/status/401").then(logCompleted)}Promise.config({ cancellation: true }); // Bluebird configwindow.Promise = Promise; // axios promises are now Bluebird flavorconst promises = [getChannels(), getContacts(), getEventActions()];Promise.all(promises)  .then(([channels, contacts, eventActions]) => {    console.log('Promise.all.then', { channels, contacts, eventActions });  })  .catch(err => {    console.log(`Promise.all.catch, '${err.message}'`)    promises.forEach(p => p.cancel());  })  .finally(() => console.log('Promise.all.finally'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script><script src="https://cdn.jsdelivr.net/bluebird/latest/bluebird.core.min.js"></script>