Vue.js async function helper
npm install vue-async-functionVue Async Function delivers a compositional API for promise resolution and data fetching. It is inspired by the hooks
functions of React Async and builds upon the
Vue Composition API that is coming with Vue 3.0. Luckily, thanks to
the official plugin you can build Vue apps with the new Composition API
today with Vue 2.5+.
- Works with promises, async/await and the Fetch API
- Provides abort and retry functions
- Supports abortable fetch by providing an AbortController signal
- Reactive retry when arguments are ref-wrapped
- Written in TypeScript, ships with type definitions
The current version expects your project to be built with vue-cli 4.0+. There's no support for
previous verions. If your project is still built with 3.x, consider upgrading it. There's a detailed
upgrading guide available.
In your Vue project, you need to install @vue/composition-api together with vue-async-function:
``bash`
npm install --save @vue/composition-api vue-async-function
Or with Yarn:
`bash`
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:
`javascript
import Vue from "vue";
import VueCompositionApi from "@vue/composition-api";
Vue.use(VueCompositionApi);
`
After that, you can import useAsync or useFetch:
`javascript`
import { useAsync, useFetch } from "vue-async-function";
Inside your setup() function you can call useAsync and provide it a function that returns a Promise as its firstuseAsync
argument. returns three ref-wrapped properties: isLoading, error and data. These are reactively updatedretry
to match the state of the asynchronous process while resolution takes place. You also get two functions, andabort that respectively retry the original asynchronous function or abort the current running function.
You can choose to return any of these values to use them in the component template or pass them to other functions to
'compose' your component. A simple example:
`javascript`
export default {
setup() {
const { data, error, isLoading, retry, abort } = useAsync(someAsyncFunc);
// ...
return { data, error, isLoading, retry, abort };
}
};
The second argument of useAsync is optional. If provided, it is passed as first argument to the Promise returning
function.
`javascript`
export default {
setup() {
return useAsync(someAsyncFunc, { id: 9000 });
}
};
useAsync calls the asynchronous function for you with the optional first argument. Its second argument is an instance
of an AbortController signal. Your function
should listen to the 'abort' event on the signal and cancel its behavior when triggered.
In the following example, the wait function simply waits for a configurable period and then resolves to a string. Ifabort
the function returned from the useAsync call is triggered, the timeout is cleared and the promise won't
resolve. It is up to you to decide if the promise needs to be rejected as well.
`javascriptDone waiting ${millis} milliseconds!
async function wait({ millis }, signal) {
return new Promise(resolve => {
const timeout = setTimeout(
() => resolve(),
millis
);
signal.addEventListener("abort", () => clearTimeout(timeout));
});
}
export default {
setup() {
return useAsync(wait, { millis: 10000 });
}
};
`
Note: calling retry while the asynchronous function is not resolved calls abort as well.
If you want your application to reactively respond to changing input values for useAsync, you can pass in aref-wrapped value as well as any parameter.
An example:
`javascript
import { ref } from "@vue/composition-api";
// ...
export default {
setup() {
const wrappedAsyncFunc = ref(someAsyncFunc);
const wrappedParams = ref({ id: 9000 });
const { data, error, isLoading, retry, abort } = useAsync(someAsyncFunc);
// ...
watch(someVal, () => {
wrappedAsyncFunc.value = someOtherAsyncFunc; // triggers retry
// or
wrappedParams.value = { id: 10000 }; // triggers retry
});
// ...
return { data, error, isLoading, retry, abort };
}
};
`
Note that the triggered retry is the same as when you call retry explicitly, so it will also call abort for
unresolved functions.
With useAsync you could wrap the Fetch API easily. An example:
`javascripthttps://swapi.co/api/starships/${id}/
async function loadStarship({ id }, signal) {
const headers = { Accept: "application/json" };
const res = await fetch(, {
headers,
signal
});
if (!res.ok) throw res;
return res.json();
}
export default {
setup() {
return useAsync(loadStarship, { id: 2 });
}
};
`
This is such a common pattern that there is a special function for it: useFetch. We can implement above example as
follows:
`javascript
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 });
}
};
useFetch accepts the same arguments as the browser Fetch API. It will hook up the AbortController signal for you andAccept
based on the header it will choose between returning text() or json() results.
Above example shines even more with reactive arguments:
`javascript
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 })
};
}
};
Here, the id is made reactive. The url is also reactive, using the computed function that recomputes whenever anyid
of the reactive values is changed. We return both the and all of the results of useFetch. Now we can for instanceid
bind the reactive with v-model to an input field. Whenever the input field changes, it will cause the fetch to be
retried, aborting the current fetch if unresolved.
`html
useAsync and promises
Loading...
Error!
{{ data }}
`
`html
Loading...
Error!
{{ data }}
`
`html
Loading...
Error!
{{ data }}
`
`html
useAsync and promises, with value
Loading...
Error!
{{ data }}
`
`html
useFetch with value
Loading...
Error!
{{ data }}
``
See the examples folder for a demo project with all examples in JavaScript.
See the examples-ts folder for a demo project with all examples in TypeScript.