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 config
window.Promise = Promise; // axios promises are now Bluebird flavor
const 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>
Why does it work
Promise.all() instead of axios.all()
Looking at this old axios issue Remove axios.all
and axios.spread
#1042 can see
Axios uses Promise.all under the hood...
and this
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));
can be replaced with this
Promise.all([getUserAccount(), getUserPermissions()])
.then(function ([acct, perms]) {
// Both requests are now complete
});
so we can switch to working with Promises directly and still have the same functionality.
Promises fail fast
From MDN we see
Promise.all is rejected if any of the elements are rejected. For example, if you pass in four promises that resolve after a timeout and one promise that rejects immediately, then Promise.all will reject immediately.
so in this pattern
Promise.all(...)
.then(...)
.catch(...);
.catch()
will trigger when the first promise fails (contrast to then()
which waits until all promises complete).
Composing Promise.all
and .cancel()
The pattern is pretty simple, just cancel all the promises in .catch()
(which is called on first error).
Ref this question for details Stop other promises when Promise.all() rejects
Substituting Bluebird in Vue store
This is a basic implementation in Vuex.
yarn add bluebird
import Vue from "vue";
import Vuex from "vuex";
import axios from "axios";
import Promise from 'bluebird';
Vue.use(Vuex);
Promise.config({ cancellation: true }); // Bluebird config
window.Promise = Promise; // axios promises are now Bluebird flavor
export default new Vuex.Store({
actions: {
fetchData({ dispatch }) {
function getChannels() {
return axios.get("https://reqres.in/api/users?page=1&delay=5");
}
function getContacts() {
return axios.get("https://reqres.in/api/users?page=2");
}
function getEventActions() { // 401 - auth error
return axios.get("https://httpbin.org/status/401");
}
const promises = [getChannels(), getContacts(), getEventActions()];
Promise.all(promises)
.then(([channels, contacts, eventActions]) => {
dispatch("channels/setChannels", channels.data, { root: true });
dispatch("contacts/setContacts", contacts.data, { root: true });
dispatch("events/setActions", eventActions.data, { root: true });
})
.catch(err => {
promises.forEach(p => p.cancel());
})
}
}
});