await Composition API

await Vue 3 🙏

Martin Malinda

  • TopMonks - SPAs - Ember / React
  • Leadfeeder - Remote - SPA - Ember
  • GreenFox Academy - .NET , Java, React
  • GoOut - Vue / Nuxt 🎉 

goout.cz

Praha, Vinohrady

Composition API

  • Oficiálně k dispozici ve Vue 3
  • Ve Vue2 přes plugin
  • umožňuje... composition 😎
  • useThis(), useThat()...
  • ref, computed, onMounted, onCreated...

Asynchronní logika?!

  • AJAX requests
  • scheduling
  • timeouts
  • intervals
  • polling
  • throttling
  • debouncing
  • concurrency
  • lazy loading
  • infinite scroll
  • autocomplete
  • type-ahead search
  • background reload
  • resilient AJAX
  • a tak...

User je asynchronní....

Server je asynchronní...

A my si to ještě komplikujeme (lazy loading, scheduling...)

export default defineComponent({
  setup() {
    const data = ref(null);
    axios('/api/users').then((response) => {
      data.value = response.data;
    });
    
    return { data };
  }
});

Loading substate???

Error handling???

<template>
 <div>
   <div v-for="user in data">
     {{ user.name }}
   </div>
  </div>
</template>
export default defineComponent({
  setup() {
    const data = ref(null);
    const error = ref(null);
    const isLoading = ref(true);
    axios('/api/users').then(response => {
      data.value = response.data;
      isLoading.value = false;
    }).catch(e => {
      error.value = e;
    });
    
   return { data, error, isLoading }; 
  }
});
export default defineComponent({
  setup() {
    const data = ref(null);
    const error = ref(null);
    const isLoading = ref(true);
    axios('/api/users').then(response => {
      data.value = response.data;
    }).catch(e => {
      error.value = e;
    }).finally(() => {
      isLoading.value = false;
    });
    
   return { data, error, isLoading }; 
  }
});

Retry??

export default defineComponent({
  setup() {
    const data = ref(null);
    const error = ref(null);
    const isLoading = ref(false);
    const fetchUsers = async () => {
      error.value = null;
      isLoading.value = true;
      
      try {
       isLoading.value = true;
       const response = await axios('/api/users');
       data.value = response.data;
      } catch (e) {
       error.value = e;
      } finally {
       isLoading.value = false;
      }
    };
    
   fetchUsers();
   return { data, error, isLoading, fetchUsers }; 
  }
});
<template>
  <div>
    <div v-if="error">
      <p> {{ error.message }} </p>
      <button @click="fetchUsers">Try again</button>
    </div>
    <div v-else-if="isLoading"> Loading ... </div>
    <div v-else>
      {{ data }}
    </div>
  </div>
</template>

🤔

function useAsync(cb = () => {}) {
  const data = ref(null);
  const error = ref(null);
  const isLoading = ref(false);
  const run = async () => {
    error.value = null;
    isLoading.value = false;

    try {
      isLoading.value = true;
      data.value = await cb();
    } catch (e) {
      error.value = e;
    } finally {
      isLoading.value = false;
    }
  };

  return { data, error, isLoading, run };
}
export default defineComponent({
  setup() {
    const { data, error, isLoading, run: fetchUsers } = useAsync(async () => {
      const response = await axios('/api/users');
      return data.value;
    });
    return { data, error, isLoading, fetchUsers };
  },
});
import { useAsyncState } from '@vueuse/core'
import axios from 'axios'

export default {
  setup() {
    const { state, ready } = useAsyncState(
      axios
        .get('https://jsonplaceholder.typicode.com/todos/1')
        .then(t => t.data),
      {
        id: null,
      },
    )

    return { state, ready }
  },
}

DRYing the template?

<template>
  <Promised :promise="usersPromise">
    <template v-slot:pending>
      <p>Loading...</p>
    </template>
    <template v-slot="data">
      <ul>
        <li v-for="user in data">{{ user.name }}</li>
      </ul>
    </template>
    <template v-slot:rejected="error">
      <p>Error: {{ error.message }}</p>
    </template>
  </Promised>
</template>

vue-promised

export default defineComponent({
  setup() {
    const usersPromise = axios.get('/api/users');
    return { usersPromise };
  }
});

🤔

Promises? Refs?

Ani jedno?

<Suspense />

export default defineComponent({
  async setup() {
    const users = await fetchUsers();
    return { users }; 
  }
});
<div v-for="user in users">
  {{ user.name }}
</div>
export default defineComponent({
 setup() {
  const error = ref(null);
  onErrorCaptured(caughtError => {
    error.value = caughtError;
    return true;
  })}
  return { error };
 }
});
<template>
  <div v-if="error"> {{ error.message }}</div>
   <Suspense v-else>
    <template #default>
      <Admins />
      <Users />
    </template>
    <template #fallback>
      <div>Loading...</div>
    </template>
  </Suspense>
</template>

<ChildComponent />

<ParentComponent />

Saving data?

Retry?

Concurrency management?

🤯

Flexibility?

vue-concurrency

export default defineComponent({
 setup() {
   const getUsersTask = useTask(function * () {
     const response = yield axios('/api/users');
     return response.data.users;
   });
   getUsersTask.perform();
   
   return { getUsersTask };
 }
});
<template>
  <div>
    <div v-if="getUsersTask.isRunning">
      Loading ...
    </div>
    <div v-else-if="getUsersTask.isError">
      <p>{{ getUsersTask.last.error }}</p>
      <button @click="task.perform">
        Try again
      </button>
    </div>
    <div v-else v-for="user in getUsersTask.last.value">
      {{ user.name }}
    </div>
  </div>
</template>
<template>
  <AsyncContent :task="getUsersTask" v-slot="{ lastValue }">
    <div v-for="user in lastValue">
      {{ user.name }}
    <div>
  </AsyncContent>
</template>

📖

await Composition API

By Martin Malinda