Skip to content

Instantly share code, notes, and snippets.

@LeandrodeLimaC
Last active January 5, 2021 20:07
Show Gist options
  • Save LeandrodeLimaC/03fe475c1a380b43febf0e25ff628fef to your computer and use it in GitHub Desktop.
Save LeandrodeLimaC/03fe475c1a380b43febf0e25ff628fef to your computer and use it in GitHub Desktop.
asyncHook will receive a promise and execute it returning reactive properties that will keep track of the state of your promise, you can easily store this state return in some variable to use somewhere your code
/*
* Returns a "state" reactive object, that contains the following properties
* result: Result from promise if there's any
* error: Error from promise if there's any
* isLoading: Boolean that signals if our promise is Loading or not
* retry_attempts: Tracks how many retries we already did
*
* And the following method
* retry: Will automatically retry our promise and reasign all values
*/
// If you are using VUE 3, just change the import from "@vue/composition-api" to "vue"
import { ref, reactive, watch, unref } from "@vue/composition-api";
export default function asyncHook(promiseFnc, params) {
const wrapPromiseFn = ref(promiseFnc);
const wrapParams = ref(params);
const state = reactive({
isLoading: false,
error: null,
result: null,
retry_attempts: 0,
retry: () => {
state.retry_attempts++;
const origPromiseFn = unref(wrapPromiseFn);
wrapPromiseFn.value = async (params) => origPromiseFn(params);
},
});
const watched = [wrapPromiseFn, wrapParams];
watch(
watched,
async ([promiseFnc, wrapParams]) => {
state.isLoading = true;
state.error = null;
try {
const data = await promiseFnc(wrapParams);
state.result = data;
} catch (error) {
state.error = error;
} finally {
state.isLoading = false;
}
},
{ immediate: true }
);
return state;
}
/*
*
* Inspired by useAsync hook on react, and other articles
* https://medium.com/javascript-in-plain-english/handling-asynchrony-in-vue-3-composition-api-part-1-managing-async-state-e993842ebf8f
* https://composition-api.vuejs.org/#api-introduction
* https://xebia.com/blog/next-generation-async-functions-with-vue-async-function/
* https://lukashermann.dev/writing/vuejs-async-renderless-component/
*
*/
import asyncHook from "@/services/async-hook";
import { getTodo } from "@/services/todos.service";
export default {
data: () => ({
// in order to our reactive properties work correctly we need to create those in our data
first: {},
second: {},
}),
methods: {
makeSecondRequest: async function () {
this.first = asyncHook(getTodo, { id: 4 });
// That's it!
// first.result -> Result from promise if there's any
// first.error -> Error from promise if there's any
// first.isLoading -> Boolean that signals if our promise is Loading or not
// first.retry -> Will automatically retry our promise and reasign all values
// first.retry_attempts -> Tracks how many retries we already did
},
makeThirdRequest: async function () {
this.second = asyncHook(getTodo, { id: 2 } );
// second.result
// second.error
// second.isLoading
// second.retry
// second.retry_attempts
},
},
};
import asyncHook from "@/services/async-hook";
import { getTodo } from "@/services/todos.service";
export default {
setup() {
const first = asyncHook(getTodo, { id: 2 });
// We using setup() so when we return a object, those properties will be merged in data() properties
// And for this reason we can use this as reactive
return { first };
// first.result -> Result from promise if there's any
// first.error -> Error from promise if there's any
// first.isLoading -> Boolean that signals if our promise is Loading or not
// first.retry -> Will automatically retry our promise and reasign all values
// first.retry_attempts -> Tracks how many retries we already did
},
// Just for example
export const getTodo = async ({ id }) => {
const res = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`);
if (!res.ok) throw new Error();
return res.json();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment