gpt4 book ai didi

javascript - 如何使用 watchEffect() 防止中止的异步请求破坏状态

转载 作者:行者123 更新时间:2023-12-05 00:38:44 25 4
gpt4 key购买 nike

假设我们有一个组件通过 prop 接收资源的 ID,resourceId .组件应该从 API 获取相应的资源并显示它,同时还处理加载/错误状态(遵循类似于 this one 的模式)。
从外部 API 获取资源的函数也返回 abort如果调用该函数,则会导致请求立即拒绝。当resourceId更改,任何进行中的请求都应中止以支持新请求。对于它的值(value),我正在使用 fetch()AbortController为了这。
使用 Vue 3 和组合 API,我想出了一个看起来像这样的实现:

const loading = ref(false);
const data = ref(null);
const error = ref(null);

watchEffect(async (onCancel) => {
loading.value = true;
data.value = error.value = null;

const { response, abort } = fetchResourceFromApi(props.resourceId);
onCancel(abort);

try {
data.value = await (await response).json();
} catch (e) {
error.value = e;
} finally {
loading.value = false;
}
});
<div v-if="loading">Loading...</div>
<div v-else-if="error">Error: {{ error }}</div>
<div v-else>{{ data }}</div>
这在大多数情况下都可以正常工作,但只要取消播放就会中断。如果 resourceId在最后一个 API 请求完成之前发生更改,会发生以下事件顺序:
  • abort()被调用
  • watchEffect回调被调用,设置 loading , error , 和 data
  • catchfinally调用来自原始请求的 block ,设置 loadingerror
  • 第二个 API 请求完成并设置 loadingdata

  • 这会导致意外状态,其中 loading设置为 false当第二个请求进行中时, error包含由中止第一个请求引发的异常,以及 data包含来自第二个请求的值。
    是否有任何设计模式或解决方法可以解决此问题?

    最佳答案

    如果事件的顺序如您所描述:

  • 第一个 watchEffect
  • abort
  • 第二watchEffect
  • 捕获最后的第一个watchEffect

  • 然后,您可以保留一个计数器并使用它来跟踪当前正在运行的请求是什么(实际上它是请求 id,或某些库调用它的版本)。每次 watchEffect被调用,增加计数器并拍摄当前值的快照。如果当前计数器与您开始使用的计数器值相同,请检查您的捕获并最终阻止。如果它们不匹配,则意味着您正在处理陈旧的请求错误,因此您可以跳过错误和加载状态的修改。

    const {ref, watchEffect} = Vue;

    const App = {
    setup() {
    const resourceId = ref(0);
    const loading = ref(false);
    const data = ref(null);
    const error = ref(null);

    // fake an api fetch method to asynchronously return
    // a result after 3 seconds.
    const fetchFor = (id) => {
    let abort;
    const response = new Promise((resolve, reject) => {
    abort = () => reject(`request ${id} aborted`);
    setTimeout(() => resolve({msg: "result for " + id}), 3000);
    });
    return {response, abort};
    };


    let version = 0;

    watchEffect(async (onCancel) => {
    version ++;
    const currentVersion = version;

    loading.value = true;
    data.value = error.value = null;
    const { response, abort } = fetchFor(resourceId.value);
    onCancel(abort);
    try {
    data.value = await response;
    } catch (e) {
    if (currentVersion === version) {
    error.value = e;
    }

    } finally {
    if (currentVersion === version) {
    loading.value = false;
    }
    }
    });

    return {resourceId, loading, data, error};
    }
    };

    const app = Vue.createApp(App);
    app.mount("#app");
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>

    <div id="app">
    <button @click="resourceId++">Request</button>
    <div> last request is {{resourceId}} </div>
    <div> loading = {{loading}} </div>
    <div> data = {{data?.msg}} </div>
    <div> error = {{error}} </div>
    </div>

    关于javascript - 如何使用 watchEffect() 防止中止的异步请求破坏状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72637898/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com