0๐
I understand this better now.
A Task creates one or more Instances, and those Instances are asynchronous. Therefore we need to handle Instances like any other async code (await, try/catch, then/catch, etc). I have included a few options for doing that.
These code examples replace everything below the line // Now I try to access some of the varibles
of the opening post. The catch errors have TypeScript types and avoid EsLint errors where possible:
Option 1 โ then/catch promises:
instance
.then((response) => {
console.log(response); // tada
console.log(instance.value); // tada
})
.catch((err: Error) => {
console.log(err.message); // 'Ruh oh. Something went wrong.'
console.log(instance.isError); // true
console.log(instance.error); // the Error object
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
console.log(instance.error.message); // 'Ruh oh. Something went wrong.'
});
Option 2 โ try/catch using async wrapper:
const wrapper = async () => {
try {
await instance;
console.log(instance.value); // tada
} catch (err) {
if (err instanceof Error) {
console.log(err.message); // 'Ruh oh. Something went wrong.'
}
console.log(instance.isError); // true
console.log(instance.error); // the Error object
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
console.log(instance.error.message); // 'Ruh oh. Something went wrong.'
}
};
void wrapper();
Option 3 โ IIFE:
(async () => {
await instance;
console.log(instance.value); // tada
})().catch((err: Error) => {
console.log(err.message); // 'Ruh oh. Something went wrong.'
console.log(instance.isError); // true
console.log(instance.error); // the Error object
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
console.log(instance.error.message); // 'Ruh oh. Something went wrong.'
});
And actually the original code can be modified because we are not calling the Instance multiple times.
A streamlined version could chain the useTask()
with perform()
which would then return a single Instance (as opposed to the original code which returned a Task and later assigned the Instance to a variable).
That would save some lines but also require us to update the template and remove references to the task:
<template>
<div>
<div v-if="instance.isRunning">Loading...</div>
<div v-else-if="instance.isError">{{ instance.error.message }}</div>
<div v-else>
{{ instance.value }}
</div>
</div>
</template>
<script setup lang="ts">
import { timeout, useTask } from 'vue-concurrency';
const instance = useTask(function* () {
yield timeout(1000);
if (Math.random() < 0.5) {
// lets say the API is flaky and errors out often:
throw new Error('Ruh oh. Something went wrong.');
} else {
return 'tada';
}
}).perform();
// Can include then/catch, try/catch etc here if you want to access the instance variables
</script>