If you are familiar with C# programming language, to cancel a Task in C#, we issue a call to Cancel()
method call on the CancellationTokenSource
.
Axios has a similar mechanism like C# to cancel an ongoing request.
For the purpose of this article, let’s say we will have two buttons on our page.
And a label to show the status of the current request.
There are a couple of ways to set up cancellation in axios.
let axiosSource = axios.CancelToken.source(); axios.get("https://reqres.in/api/users", { cancelToken: axiosSource.token, }) .then((res) => { this.users = res.data.data; });
We can setup cancelling a request by getting the cancel token from CancelToken.source()
factory method and passing the token to the axios API.
Once we set up the cancellation, we can cancel the request by invoking cancel()
function on the axiosSource.
axiosSource.cancel();
let CancelToken = axios.CancelToken; let cancel; axios.get('https://reqres.in/api/users', { cancelToken: new CancelToken(function executor(c) { cancel = c; // c is the cancel function }) }); cancel(); // cancel the request
When we click on the send request button, we can get the user’s data to show it in the UI with the following code.
this.updateState(states.IN_PROGRESS); let axiosSource = axios.CancelToken.source(); this.request = { cancel: axiosSource.cancel }; axios .get("https://reqres.in/api/users?delay=2", { cancelToken: axiosSource.token, }) .then((res) => { this.users = res.data.data; this.updateState(states.SUCCEEDED); });
In the reqres.in API, we can customize the delay so that we will have the ability to cancel the request before the data arrives.
In the above code, observe that we have a request
variable to capture the cancel function (ln 3). We can use this to issue a cancel request on the current request when the user clicks on the cancel button.
And once we have the data, we will set the data to the user’s array and update the current state using updateState
function.
Here is the full user component with bootstrap-vue buttons and table in the template section.
// User.vue <template> <div> <div> <b-button variant="outline-success" @click="send">Send Request</b-button> <b-button variant="outline-danger" :disabled="!requestInProgress" @click="cancelRequest" >Cancel Request</b-button > <b-button variant="outline-info" @click="reset">Reset</b-button> </div> <br /> <p>Request Status: {{ currentState }}</p> <div v-if="users.length > 0"> <div> <h4>Users in the reqres.in</h4> </div> <b-table striped bordered responsive hover :items="users" :fields="fields" > <template #cell(avatar)="data"> <img v-bind:src="data.value" :width="50" :height="50" /> </template> </b-table> </div> </div> </template> <script> import { BTable, BButton } from "bootstrap-vue"; import axios from "axios"; const states = { IDLE: "Idle 🟡", IN_PROGRESS: "In Progress 📀", SUCCEEDED: "Successful ✅", CANCELLED: "Cancelled ❌", }; const API_URL = "https://reqres.in/api/users?delay=2"; export default { name: "User", components: { BTable, BButton, }, data() { return { fields: ["id", "avatar", "first_name", "last_name", "email"], users: [], request: null, currentState: states.IDLE, }; }, methods: { cancelRequest() { this.cancel(); this.updateState(states.CANCELLED); }, send() { this.users = []; this.updateState(states.IN_PROGRESS); this.cancel(); let axiosSource = axios.CancelToken.source(); this.request = { cancel: axiosSource.cancel }; axios .get(API_URL, { cancelToken: axiosSource.token, }) .then((res) => { this.users = res.data.data; this.updateState(states.SUCCEEDED); }); }, updateState(msg) { this.currentState = msg; this.request = null; }, reset() { this.currentState = states.IDLE; this.cancel(); this.request = null; this.users = []; }, cancel() { if (this.request) this.request.cancel(); }, }, computed: { requestInProgress() { return this.request; }, }, }; </script>
And here is the demo of it.
If you know how to do it with Vuex you can skip the next section. Thanks for reading! [icon name=”hand-peace” prefix=”far”]
Let’s see what goes into the Vuex state
So, here is our module.js
file with the initial state setup
const states = { IDLE: "Idle 🟡", IN_PROGRESS: "In Progress 📀", SUCCEEDED: "Successful ✅", CANCELLED: "Cancelled ❌", }; const state = { cancelRequest: null, users: [], currentStatus: states.IDLE }; const getters = { requestInProgress (state) { if (state.cancelRequest) { return true; } return false; } };
Let’s see what goes into our mutations.
const mutations = { addRequest (state, payload) { state.users = []; state.cancelRequest = payload; updateStatus(state, states.IN_PROGRESS); }, cancelRequest (state) { if (state.cancelRequest) { state.cancelRequest(); } state.cancelRequest = null; updateStatus(state, states.CANCELLED); }, updateUserData (state, payload) { state.users = []; payload.data.forEach(user => { state.users.push(user); }); state.cancelRequest = null; updateStatus(state, states.SUCCEEDED); } }; function updateStatus (state, msg) { state.currentStatus = msg; }
And here is our users component invoking the mutations.
<template> <div> <div> <b-button variant="outline-success" @click="getUsersAsync">Send Request</b-button> <b-button variant="outline-danger" :disabled="!requestInProgress" @click="cancelRequest" >Cancel Request</b-button > </div> <br /> <p>Request Status: {{ currentStatus }}</p> <div v-if="users.length > 0"> <div> <h4>Users in the reqres.in</h4> </div> <b-table striped bordered responsive hover :items="users" :fields="fields" > <template #cell(avatar)="data"> <img v-bind:src="data.value" :width="50" :height="50" /> </template> </b-table> </div> </div> </template> <script> import { BTable, BButton } from "bootstrap-vue"; import { mapActions, mapGetters, mapMutations, mapState } from "vuex"; export default { name: "Users", components: { BTable, BButton, }, data() { return { fields: ["id", "avatar", "first_name", "last_name", "email"], }; }, methods: { ...mapActions("user", ["getUsersAsync"]), ...mapMutations("user", ["cancelRequest"]) }, computed: { ...mapState("user", ["users", "currentStatus"]), ...mapGetters("user", ["requestInProgress"]) }, }; </script>
So, in the above user component, we don’t have much to read in the methods/computed listeners as we moved all the operations to mutations.
When we click on send request button we will call getUsersAsync
action. Clicking the cancel request button will call cancelRequest
mutation.
Here is our actions.js
file that makes request to user service.
// actions.js import userService from '../../services/userService'; export const getUsersAsync = async ({ commit }) => { let response = await userService.getUsersAsync(); commit('updateUserData', response.data); };
I wrote the axios call in separate userService.js
file and here it is.
// userService.js import axios from 'axios'; import userStore from '../store'; export default { async getUsersAsync () { const source = axios.CancelToken.source(); userStore.commit('user/addRequest', source.cancel); return axios({ method: 'get', url: 'https://reqres.in/api/users?delay=2', cancelToken: source.token }); } }
Here we will get the source and commit our cancel function to our user store (that’s why we import userStore
). Now, we should have our cancel function stored in the Vuex state.
That’s it! Here is the complete source code.
I saw this note in the axios documentation. But, I haven’t tried yet.
Note: you can cancel several requests with the same cancel token.
from https://axios-http.com/docs/cancellation
So, if we have multiple requests going at once and if we want to cancel all of them sometime later, then we can create a token (from CancelToken.source()
) and pass it to all the requests.
And if we invoke cancel()
then all the requests to which we have set the token will be cancelled at once.
Karthik is a passionate Full Stack developer working primarily on .NET Core, microservices, distributed systems, VUE and JavaScript. He also loves NBA basketball so you might find some NBA examples in his posts and he owns this blog.
In this post, we’ll see how to test gRPC Server applications using different clients. And… Read More
In this post, we'll create a new gRPC project in ASP.NET Core and see what's… Read More
In this blog post, we’ll see how to run dotnet core projects without opening visual… Read More
Programmatically evaluating policies is useful when we want to provide access or hide some data… Read More
We saw how we could set up policy-based authorization in our previous article. In this… Read More
What is policy-based authorization and how to set up policy-based authorization with handlers and policies… Read More
This website uses cookies.