成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

聊聊 UseEffect 怎么支持Async...Await

開發 前端
useEffect 作為 Hooks 中一個很重要的 Hooks,可以讓你在函數組件中執行副作用操作。它能夠完成之前 Class Component 中的生命周期的職責。

背景

大家在使用 useEffect 的時候,假如回調函數中使用 async...await... 的時候,會報錯如下。

圖片

看報錯,我們知道 effect function 應該返回一個銷毀函數(effect:是指return返回的cleanup函數),如果 useEffect 第一個參數傳入 async,返回值則變成了 Promise,會導致 react 在調用銷毀函數的時候報錯。

React 為什么要這么做?

useEffect 作為 Hooks 中一個很重要的 Hooks,可以讓你在函數組件中執行副作用操作。它能夠完成之前 Class Component 中的生命周期的職責。它返回的函數的執行時機如下:

  • 首次渲染不會進行清理,會在下一次渲染,清除上一次的副作用。
  • 卸載階段也會執行清除操作。

不管是哪個,我們都不希望這個返回值是異步的,這樣我們無法預知代碼的執行情況,很容易出現難以定位的 Bug。所以 React 就直接限制了不能 useEffect 回調函數中不能支持 async...await...

useEffect 怎么支持 async...await...

竟然 useEffect 的回調函數不能使用 async...await,那我直接在它內部使用。

做法一:創建一個異步函數(async...await 的方式),然后執行該函數。

useEffect(() => {
const asyncFun = async () => {
setPass(await mockCheck());
};
asyncFun();
}, []);

做法二:也可以使用 IIFE,如下所示:

useEffect(() => {
(async () => {
setPass(await mockCheck());
})();
}, []);

自定義 hooks

既然知道了怎么解決,我們完全可以將其封裝成一個 hook,讓使用更加的優雅。我們來看下 ahooks 的 useAsyncEffect,它支持所有的異步寫法,包括 generator function。

思路跟上面一樣,入參跟 useEffect 一樣,一個回調函數(不過這個回調函數支持異步),另外一個依賴項 deps。內部還是 useEffect,將異步的邏輯放入到它的回調函數里面。

function useAsyncEffect(
effect: () => AsyncGenerator<void, void, void> | Promise<void>,
// 依賴項
deps?: DependencyList,
) {
// 判斷是 AsyncGenerator
function isAsyncGenerator(
val: AsyncGenerator<void, void, void> | Promise<void>,
): val is AsyncGenerator<void, void, void> {
// Symbol.asyncIterator: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator
// Symbol.asyncIterator 符號指定了一個對象的默認異步迭代器。如果一個對象設置了這個屬性,它就是異步可迭代對象,可用于for await...of循環。
return isFunction(val[Symbol.asyncIterator]);
}
useEffect(() => {
const e = effect();
// 這個標識可以通過 yield 語句可以增加一些檢查點
// 如果發現當前 effect 已經被清理,會停止繼續往下執行。
let cancelled = false;
// 執行函數
async function execute() {
// 如果是 Generator 異步函數,則通過 next() 的方式全部執行
if (isAsyncGenerator(e)) {
while (true) {
const result = await e.next();
// Generate function 全部執行完成
// 或者當前的 effect 已經被清理
if (result.done || cancelled) {
break;
}
}
} else {
await e;
}
}
execute();
return () => {
// 當前 effect 已經被清理
cancelled = true;
};
}, deps);
}

async...await 我們之前已經提到了,重點看看實現中變量 cancelled 的實現的功能。它的作用是中斷執行。

通過 yield 語句可以增加一些檢查點,如果發現當前 effect 已經被清理,會停止繼續往下執行。

試想一下,有一個場景,用戶頻繁的操作,可能現在這一輪操作 a 執行還沒完成,就已經開始開始下一輪操作 b。這個時候,操作 a 的邏輯已經失去了作用了,那么我們就可以停止往后執行,直接進入下一輪操作 b 的邏輯執行。這個 cancelled 就是用來取消當前正在執行的一個標識符。

還可以支持 useEffect 的清除機制么?

可以看到上面的 useAsyncEffect,內部的 useEffect 返回函數只返回了如下:

return () => {
// 當前 effect 已經被清理
cancelled = true;
};

這說明,你通過 useAsyncEffect 沒有 useEffect 返回函數中執行清除副作用的功能。

你可能會覺得,我們將 effect(useAsyncEffect 的回調函數)的結果,放入到 useAsyncEffect 中不就可以了?

實現最終類似如下:

function useAsyncEffect(effect: () => Promise<void | (() => void)>, dependencies?: any[]) {
return useEffect(() => {
const cleanupPromise = effect()
return () => { cleanupPromise.then(cleanup => cleanup && cleanup()) }
}, dependencies)
}

這種做法在這個 issue 中有討論,上面有個大神的說法我表示很贊同:

圖片

他認為這種延遲清除機制是不對的,應該是一種取消機制。否則,在鉤子已經被取消之后,回調函數仍然有機會對外部狀態產生影響。他的實現和例子我也貼一下,跟 useAsyncEffect 其實思路是一樣的,如下:

實現:

function useAsyncEffect(effect: (isCanceled: () => boolean) => Promise<void>, dependencies?: any[]) {
return useEffect(() => {
let canceled = false;
effect(() => canceled);
return () => { canceled = true; }
}, dependencies)
}

Demo:

useAsyncEffect(async (isCanceled) => {
const result = await doSomeAsyncStuff(stuffId);
if (!isCanceled()) {
// TODO: Still OK to do some effect, useEffect hasn't been canceled yet.
}
}, [stuffId]);

其實歸根結底,我們的清除機制不應該依賴于異步函數,否則很容易出現難以定位的 bug。

總結與思考

由于 useEffect 是在函數式組件中承擔執行副作用操作的職責,它的返回值的執行操作應該是可以預期的,而不能是一個異步函數,所以不支持回調函數 async...await 的寫法。

我們可以將 async...await 的邏輯封裝在 useEffect 回調函數的內部,這就是 ahooks useAsyncEffect 的實現思路,而且它的范圍更加廣,它支持的是所有的異步函數,包括 generator function。

參考

  • issue: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30551
  • React useEffect 不支持 async function 你知道嗎?
  • https://zhuanlan.zhihu.com/p/425129987。
責任編輯:姜華 來源: 前端雜貨鋪
相關推薦

2021-08-18 07:05:57

ES6Asyncawait

2021-06-28 07:27:43

AwaitAsync語法

2021-06-15 05:36:45

Gulpawaitasync

2016-10-27 17:05:32

Chrome瀏覽器

2014-07-15 10:31:07

asyncawait

2016-11-22 11:08:34

asyncjavascript

2012-07-22 15:59:42

Silverlight

2023-10-08 10:21:11

JavaScriptAsync

2021-07-20 10:26:12

JavaScriptasyncawait

2023-07-28 07:31:52

JavaScriptasyncawait

2024-12-30 08:22:35

2022-08-27 13:49:36

ES7promiseresolve

2024-12-23 08:00:45

2024-11-11 11:33:57

2022-06-16 10:37:09

asyncawait

2017-04-10 15:57:10

AsyncAwaitPromise

2024-04-08 00:00:00

asyncawaiPromise

2022-02-10 09:04:50

架構

2022-11-21 09:01:00

Swift并發結構

2014-07-15 10:08:42

異步編程In .NET
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 99爱视频 | 国产精品久久久久aaaa | 黄色在线免费观看 | 二区欧美 | 日韩成人高清在线 | 久久久久一区二区三区 | 亚洲精彩免费视频 | 夜夜爽99久久国产综合精品女不卡 | 精品国产1区2区3区 在线国产视频 | av无遮挡 | 亚洲福利网 | 久久精品国产99国产精品亚洲 | 欧美888| 日韩国产精品一区二区三区 | 91麻豆精品国产91久久久资源速度 | 黄色一级在线播放 | 欧洲亚洲精品久久久久 | 中文字幕日韩欧美 | 欧美日韩国产中文 | 欧美二区在线 | 91视视频在线观看入口直接观看 | 女同久久 | 国产有码| 国产91中文 | 91porn成人精品 | 日日干夜夜草 | 凹凸日日摸日日碰夜夜 | 黑人巨大精品欧美黑白配亚洲 | 国产四区 | 精品国产一区久久 | 亚洲永久入口 | www.蜜桃av | 在线观看中文字幕dvd播放 | 日本精品视频一区二区 | 国产欧美在线 | 亚洲国产一区二区三区在线观看 | 欧美日韩三级视频 | 国产精品视频在线观看 | 欧美在线成人影院 | 日韩精品免费在线观看 | 国产精品夜色一区二区三区 |