Managing async operations statuses in your Vue components
npm install vue-async-operations
import Vue from 'vue'
import VueAsyncOperations from 'vue-async-operations'
Vue.use(VueAsyncOperations)
Then, in your component options, provide an asyncOperations
object where each key is a name of an async operation and the value is a function that returns Promise
:
//...
asyncOperations: {
someAsyncStuff () {
// return Promise
// ☝️vm instance is binded to function as `this` context
}
}
//...
Or you can link operation to some method in component:
//...
asyncOperations: {
someAsyncStuff: 'someMethodName'
}
//...
Then, trigger an operation in your component e.g. in created
hook:
//...
created () {
this.$async.someAsyncStuff.$perform()
}
//...
And use operation performing state in the template:
<div v-if="$async.someAsyncStuff.$pending">
<!-- render a loader while async operation is pending -->
</div>
<!-- OR -->
<div v-if="$async.someAsyncStuff.$resolved">
<!-- some stuff that shouldn't be rendered until async operation finished -->
</div>
<!-- OR -->
<div v-if="$async.someAsyncStuff.$rejected">
<!-- some stuff that should be displayed if async operation failed -->
</div>
The function that defines an operation may return an array of promises, e.g.:
//...
asyncOperations: {
someCompositAsyncStuff () {
return [
this.someVuexAction(),
this.someAnotherVuexAction()
]
}
}
//...
This way, the operation state handler, that placed under the hood of this plugin, will operates via Promise.all([])
so reactive states of operation will be changed only when last promise will be resolved or rejected.
Also, you can define as much separate operations as you need:
//...
asyncOperations: {
asyncStuff1 () { //... },
asyncStuff2 () { //... },
asyncStuff3 () { //... },
...
}
//...
You can pass arguments to $perform()
method and receive them in operation function:
//...
asyncOperations: {
// some operation defined as function
stuff1 (a, b, c) {
console.log(a, b, c) // 1, 2, 3
},
// another operation defined as link to some method of a component
stuff2: 'loadStuff2'
},
methods: {
loadStuff2 (d, e, f) {
console.log(d, e, f) // 4, 5, 6
}
},
created () {
this.$async.stuff1.$perform(1, 2, 3)
this.$async.stuff2.$perform(4, 5, 6)
}
//...
The .$perform()
method of an operation returns Promise
and passes the result of the original promise into resolve
and reject
methods:
//...
created () {
this.$async.someAsyncStuff.$perform().then(
result => { // handle a result },
err => { // handle an error },
finally => { // handle the finish of an operation }
)
}
//...
If your operation returns an array of promises, the result will contain an array of results in the order they were defined in the original array
Also, you can handle the result directly in operation function:
//...
asyncOperations: {
someOperation: 'someMethod'
},
methods: {
someMethod () {
this.$api.someApiCall().then(
result => { // handle the result },
error => { // handle an error }
)
}
},
created () {
this.$async.someOperation.$perform()
}
//...
If you have several async operations that are leading to some common result and you need to track their reactive statuses separately but also you wanna have an aggregated reactive status for whole batch, you can compose your operations the following way:
//...
asyncOperations: {
allAsyncStuff: {
asyncStuff1 () {},
asyncStuff2 () {}
}
}
//...
Then, use separate and aggregated reactive statuses
<div v-if="$async.allAsyncStuff.$pending"> Some stuff is still loading... </div>
<div v-else-if="$async.allAsyncStuff.$resolved"> All stuff loaded </div>
<div v-if="$async.allAsyncStuff.asyncStuff1.$resolved"> Stuff 1 is loaded </div>
<div v-if="$async.allAsyncStuff.asyncStuff2.$resolved"> Stuff 2 is loaded </div>
//...
asyncOperations: {
allAsyncStuff: {
asyncStuff1 (a, b, c) {
console.log(a, b, c) // 1, 2, 3
},
asyncStuff2 ({a, b, c}) {
console.log(a, b, c) // 4, 5, 6
}
}
},
created () {
this.$async.allAsyncStuff.$perform({
asyncStuff1: [1, 2, 3], // pass args to `asyncStuff1`
asyncStuff2: {a: 4, b: 5, c: 6} // pass args to `asyncStuff2`
})
}
//...
$pending
$resolved
$rejected
$err
$perform
You can customize some stuff:
Vue.use(VueAsyncOperations, {
mixinPrefix,
dataPropName,
computedPropName,
componentOptionName
})
-
mixinPrefix
- plugin adds to your application a global mixin which injects property with operations states into the componentsdata
and according to official vuejs style guide this property is prefixed, but you can change this prefix if it's necessary for some reason -
dataPropName
- actually the name of the prop mentioned above -
computedPropName
- the name of the computed prop you use for getting acces to operations for getting its states and calling$perform()
method -
componentOptionName
- the name of the component option where you define operations
Plugin defaults are:
{
mixinPrefix: 'vueAsyncOps_',
dataPropName: 'async',
computedPropName: '$async',
componentOptionName: 'asyncOperations'
}