Thanks to the Vue Composition API plugin you can enjoy building next generation Vue apps today with Vue2.x. Vue Async Function builds upon the Composition API and brings you simple helpers to handle all your asynchronous needs, in a similar fashion to the hooks functions of React Async.
- Works with promises, async/await and the Fetch API
- Provides
abort
andretry
functions - Supports abortable fetch by providing an AbortController signal
- Reactive retry when arguments are
ref
-wrapped
In your Vue 2.x project, you'll need to install @vue/composition-api
together with vue-async-function
:
npm install --save @vue/composition-api vue-async-function
Or with Yarn:
yarn add @vue/composition-api vue-async-function
Then modify your entrypoint (often main.js
or main.ts
), as stated in the
Vue Composition API docs:
import Vue from "vue";
import VueCompositionApi from "@vue/composition-api";
Vue.use(VueCompositionApi);
After that, you can import useAsync
or useFetch
:
import { useAsync, useFetch } from "vue-async-function";
When you call useAsync
or useFetch
inside your setup()
function, you retrieve three reactive properties and return
these to use in your template. You also get two functions, retry
and abort
that respectively retry the original
asynchronous function or abort the current running function. Retrying a function while the original asynchronous
function is still running aborts it.
setup() {
const { data, error, isLoading, retry, abort } = useAsync(someAsyncFunc);
// ...
return { data, error, isLoading, retry, abort };
}
The second argument of useAsync
is passed as first argument to the promise.
setup() {
return useAsync(someAsyncFunc, { id: 9000 });
}
If you want your application to reactively respond to changing input values for useAsync
or useFetch
, you can pass
in a ref
value as well as any parameter.
import { ref } from "@vue/composition-api";
// ...
setup() {
const wrappedAsyncFunc = ref(someAsyncFunc);
const wrappedParams = ref({ id: 9000 });
const { data, error, isLoading, retry, abort } = useAsync(someAsyncFunc);
// ...
watch(someVal, () => {
wrappedAsyncFunc.value = async () => {}; // triggers retry
// or
wrappedParams.value = { id: 10000 } // triggers retry
})
// ...
return { data, error, isLoading, retry, abort };
}
<template>
<div id="root">
<h2>useAsync and promises</h2>
<div v-if="isLoading">Loading...</div>
<div v-else-if="error">Error!</div>
<pre v-else>{{ data }}</pre>
</div>
</template>
<script>
import { useAsync } from "vue-async-function";
async function wait({ millis }) {
return new Promise(resolve => {
setTimeout(() => resolve(`Done waiting ${millis} milliseconds!`), millis);
});
}
export default {
setup() {
return useAsync(wait, { millis: 2000 });
}
};
</script>
<template>
<button @click="retry" :disabled="isLoading">Retry</button>
<button @click="abort" :disabled="!isLoading">Abort</button>
<div v-if="isLoading">Loading...</div>
<div v-else-if="error">Error!</div>
<pre v-else>{{ data }}</pre>
</template>
<script>
import { useAsync } from "vue-async-function";
async function loadStarship({ id }, signal) {
const headers = { Accept: "application/json" };
const res = await fetch(`https://swapi.co/api/starships/${id}/`, {
headers,
signal
});
if (!res.ok) throw res;
return res.json();
}
export default {
setup() {
return useAsync(loadStarship, { id: 2 });
}
};
</script>
<template>
<button @click="retry" :disabled="isLoading">Retry</button>
<button @click="abort" :disabled="!isLoading">Abort</button>
<div v-if="isLoading">Loading...</div>
<div v-else-if="error">Error!</div>
<pre v-else>{{ data }}</pre>
</template>
<script>
import { useFetch } from "vue-async-function";
export default {
setup() {
const id = 9;
const url = `https://swapi.co/api/starships/${id}/`;
const headers = { Accept: "application/json" };
return useFetch(url, { headers });
}
};
</script>
<template>
<div id="root">
<h2>useAsync and promises, with value</h2>
<button @click="retry">Retry</button>
<button @click="abort">Abort</button>
<div v-if="isLoading">Loading...</div>
<div v-else-if="error">Error!</div>
<pre v-else>{{ data }}</pre>
</div>
</template>
<script>
import { useAsync } from "vue-async-function";
import { ref, watch } from "@vue/composition-api";
async function wait({ millis }, signal) {
return new Promise(resolve => {
const timeout = setTimeout(
() => resolve(`Done waiting ${millis} milliseconds!`),
millis
);
signal.addEventListener("abort", () => clearTimeout(timeout));
});
}
export default {
setup(props) {
const wrapParams = ref();
// watch incoming props change
watch(
() => props.ms,
millis => {
wrapParams.value = { millis };
}
);
return useAsync(wait, wrapParams);
},
props: {
ms: { type: Number, required: true }
}
};
</script>
<template>
<div id="root">
<h2>useFetch with value</h2>
<label>
Ship ID:
<input v-model="id" type="number" />
</label>
<button @click="retry">Retry</button>
<button @click="abort">Abort</button>
<div v-if="isLoading">Loading...</div>
<div v-else-if="error">Error!</div>
<pre v-else>{{ data }}</pre>
</div>
</template>
<script>
import { useFetch } from "vue-async-function";
import { ref, computed } from "@vue/composition-api";
export default {
setup() {
const id = ref(2);
const computedUrl = computed(
() => `https://swapi.co/api/starships/${id.value}/`
);
const headers = { Accept: "application/json" };
return {
id,
...useFetch(computedUrl, { headers })
};
}
};
</script>
See the examples folder for a demo project with all examples.