关于javascript:Vue3用组合编写更好的代码Async-Without-Await-模式44

3次阅读

共计 4014 个字符,预计需要花费 11 分钟才能阅读完成。

如果能让异步代码正确工作,它能够大大简化咱们代码。然而,解决这种额定的复杂性,特地是与可合一起,可能会令人困惑。这篇文章介绍了无期待的异步模式。这是一种在组合中编写异步代码的办法,而不像通常那样令人头疼。

无期待的异步

用组合 API 编写异步行为有时会很麻烦。所有的异步代码必须在任何反应式代码之后的设置函数的末端。如果你不这样做,它可能会烦扰你的反馈性。

setup 函数运行到一个 await 语句时,它将返回。一旦它返回,该组件就会被挂载,并且应用程序会像平常一样继续执行。任何在 await 之后定义的响应式,无论是 computedwatcher,还是其余什么,都还没有被初始化。

这意味着,一个在 await 之后定义的计算属性一开始不会被模板应用。相同,只有在异步代码实现,setup 函数实现执行后,它才会存在。

然而,有一种办法能够编写异步组件,能够在任何中央应用,而不须要这些麻烦。

const count = ref(0);
// 这种异步数据获取不会烦扰咱们的响应式
const {state} = useAsyncState(fetchData());
const doubleCount = computed(() => count * 2);

实现没有期待的异步模式

为了实现这一模式,咱们将同步地挂起所有的响应式值。而后,每当异步代码实现后,这些值将被异步更新。

首先,咱们须要把咱们的状态筹备好并返回。咱们将用一个 null 的值来初始化,因为咱们还不晓得这个值是什么。

export default useMyAsyncComposable(promise) {const state = ref(null);
  return state;
}

第二,咱们创立一个办法,期待咱们的 promise,而后将后果设置为 state:

const execute = async () => {state.value = await promise;}

每当这个 promise 返回时,它就会被动更新咱们的 state。

当初咱们只须要把这个办法增加到组合中。

export default useMyAsyncComposable(promise) {const state = ref(null);

  // Add in the execute method...
  const execute = async () => {state.value = await promise;}

  // ...and execute it!
  execute();

  return state;
}

咱们在从 useMyAsyncComposable 办法返回之前调用了 execute 函数。然而,咱们并没有应用 await 关键字。

当咱们进行并期待 execute 办法中的 promise 时,执行流立刻返回到 useMyAsyncComposable 函数。而后它继续执行 execute() 语句并从可组合对象返回。

export default useMyAsyncComposable(promise) {const state = ref(null);

  const execute = async () => {
    // 2. 期待 promise 执行实现
    state.value = await promise

    // 5. 一段时间后...
    // Promise 执行完,state 更新
    // execute 执行实现
  }

  // 1. 执行 `execute` 办法
  execute();
  // 3.  await 将控制权返回到这一点上。// 4. 返回 state 并继续执行 "setup" 办法
  return state;
}

promise 在后盾执行,因为咱们没有期待它,所以它不会在 setup 函数中中断流。咱们能够将此可组合搁置在任何中央,而不影响响应性。

让咱们看看 VueUse 中一些组合是如何实现这种模式的。

useAsyncState

useAsyncState 能够让咱们在任何中央执行任何异步办法,并取得响应性的更新后果。

const {state, isLoading} = useAsyncState(fetchData());

在查看源代码时,能够看到它实现了这种准确的模式,但具备更多的个性,并能更好地解决边界状况。

上面是 useAsyncState 的一个简化版:

export function useAsyncState(promise, initialState) {const state = ref(initialState);
  const isReady = ref(false);
  const isLoading = ref(false);
  const error = ref(undefined);

  async function execute() {
    error.value = undefined;
    isReady.value = false;
    isLoading.value = true;

    try {
      const data = await promise;
      state.value = data;
      isReady.value = true;
    }
    catch (e) {error.value = e;}

    isLoading.value = false;
  }

  execute();

  return {
    state,
    isReady,
    isLoading,
    error,
  };
}

这个可组合的零碎还返回 isReady,通知咱们数据何时被取走。咱们还失去了isLoadingerror,以跟踪咱们的加载和谬误状态。

当初来看看另一个可组合,我认为它有一个迷人的实现形式。

useAsyncQueue

如果传给 useAsyncQueue 一个 promise 函数数组,它会按程序执行每个函数。所以,在开始下一个工作之前,会期待前一个工作的实现。为了应用更灵便,它上一个工作的后果作为输出传给下一个工作。

const {result} = useAsyncQueue([getFirstPromise, getSecondPromise]);

上面是一个官网的例子:

const getFirstPromise = () => {
  // Create our first promise
  return new Promise((resolve) => {setTimeout(() => {resolve(1000);
    }, 10);
  });
};

const getSecondPromise = (result) => {return new Promise((resolve) => {setTimeout(() => {resolve(1000 + result);
    }, 20);
  });
};

const {activeIndex, result} = useAsyncQueue([
  getFirstPromise,
  getSecondPromise
]);

即便它在异步执行代码,咱们也不须要应用await。即便在外部,可组合的程序也不应用await。相同,咱们在 “ 后盾 ” 执行这些 promise,并让后果响应式更新。

让咱们看看这个组合是如何工作的。

// 初始一些默认值
const initialResult = Array.from(new Array(tasks.length), () => ({
  state: promiseState.pending,
  data: null,
});

// 将默认值变成响应式
const result = reactive(initialResult);

// 申明一个响应式的下标
const activeIndex = ref(-1);

次要的性能是由一个 reduce 来反对的,它一一解决每个性能

tasks.reduce((prev, curr) => {return prev.then((prevRes) => {if (result[activeIndex.value]?.state === promiseState.rejected && interrupt) {onFinished();
      return;
    }

    return curr(prevRes).then((currentRes) => {updateResult(promiseState.fulfilled, currentRes);
      activeIndex.value === tasks.length - 1 && onFinished();
      return currentRes;
    })
  }).catch((e) => {updateResult(promiseState.rejected, e);
    onError();
    return e;
  })
}, Promise.resolve());

Reduce 办法有点简单,咱们拆解一下,一个个看:

tasks.reduce((prev, curr) => {// ...}, Promise.resolve());

而后,开始解决每个工作。通过在前一个 promise 根底上链接一个 .then 来实现这个工作。如果 promise 被回绝,就提前停止并返回。

if (result[activeIndex.value]?.state === promiseState.rejected && interrupt) {onFinished();
  return;
}

如果不提前终止,则执行下一个工作,并传递上一个 promise 的后果。咱们还调用 updateResult 办法,将其增加到该组合返回的 result 数组中

return curr(prevRes).then((currentRes) => {updateResult(promiseState.fulfilled, currentRes);
  activeIndex.value === tasks.length - 1 && onFinished();
  return currentRes;
});

正如你所看到的,该可组合实现了 Async Without Await 模式,但该模式只是整个可组合的几行。所以它不须要很多额定的工作,只有记住把它放在适当的地位

总结

如果咱们应用 Async Without Await 模式,咱们能够更容易地应用异步组合。这种模式能够让咱们把异步代码放在咱们想放的中央,而不必放心毁坏响应应性。

正文完
 0