Skip to content

Instantly share code, notes, and snippets.

@c5n8
Last active Jun 23, 2020
Embed
What would you like to do?
Vue Promise Builder
<template>
<div>
<slot
:pending="pending"
:fulfilled="fulfilled"
:rejected="rejected"
:settled="settled"
:result="result"
:error="error"
/>
<slot v-if="pending" name="pending" />
<slot v-else-if="fulfilled" name="fulfilled" :result="result" />
<slot v-else-if="rejected" name="rejected" :error="error" />
<slot v-if="settled" name="settled" />
</div>
</template>
<script>
import { reactive, toRefs, watch, computed } from '@vue/composition-api'
const PromiseStatus = Object.freeze({
pending: Symbol('pending'),
fulfilled: Symbol('fulfilled'),
rejected: Symbol('rejected'),
})
export default {
props: {
promise: {
type: Promise,
required: true,
},
},
setup (props, { emit }) {
const state = reactive({
status: PromiseStatus.pending,
result: null,
error: null,
pending: computed(() => state.status === PromiseStatus.pending),
fulfilled: computed(() => state.status === PromiseStatus.fulfilled),
rejected: computed(() => state.status === PromiseStatus.rejected),
settled: computed(() => state.status !== PromiseStatus.pending),
})
watch(
() => props.promise,
async (promise) => {
state.status = PromiseStatus.pending
state.result = null
state.error = null
try {
state.result = await promise
state.status = PromiseStatus.fulfilled
emit('fulfilled', state.result)
} catch (error) {
state.error = error
state.status = PromiseStatus.rejected
emit('rejected', state.error)
throw error
} finally {
emit('settled')
}
},
{
immediate: true,
},
)
return {
...toRefs(state),
}
},
}
</script>
<template>
<PromiseBuilder
:promise="promise"
@fulfilled="onFullfilled()"
@rejected="onRejected()"
@settled="onSettled()"
>
<template #pending>
Processing...
</template>
<template #fulfilled="{ result }">
{{ result }}
</template>
<template #rejected="{ error }">
{{ error.message }}
</template>
<template #settled>
<button @click="promise = asyncFunction()">
Retry
</button>
</template>
</PromiseBuilder>
</template>
<template>
<PromiseBuilder
:promise="promise"
@fulfilled="onFullfilled()"
@rejected="onRejected()"
@settled="onSettled()"
>
<template v-if="snapshot.pending">
Processing...
</template>
<template v-else-if="snapshot.fulfilled">
{{ snapshot.result }}
</template>
<template v-else-if="snapshot.rejected">
{{ snapshot.error.message }}
</template>
<template v-if="snapshot.settled">
<button @click="promise = asyncFunction()">
Retry
</button>
</template>
</PromiseBuilder>
</template>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment