1. 本际云推荐 - 专业推荐VPS、服务器,IDC点评首页
  2. 云主机运维
  3. VPS运维

useEffect支持async及await如何运用

背景

在使用useEffect中用啦回调函数中使用async…await…这时候就会报错。

useEffect支持async及await如何运用

上面代码可以看到,在报错,effectfunction应该返回一个销毁函数(effect:是指return返回的cleanup函数),如果useEffect第一个参数传入async,返回值则变成了Promise,结果就是会导致react在调用销毁函数的时候报错。

React为什么要这么做?

useEffect作为Hooks中一个很重要的Hooks,它能够让你在函数组件中执行副作用操作。也能够完成之前ClassComponent中的生命周期的职责。它返回的函数的执行时机如下:

  • 首次渲染不会进行清理,会在下一次渲染,清除上一次的副作用。
  • 卸载阶段也会执行清除操作。

不管怎样,返回值是异步,这样我们无法预知代码的执行情况,很容易出现难以定位的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,它支持所有的异步写法,包括generatorfunction。

思路跟上面一样,入参跟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)
}

上面方法很多大神读赞成:上面不是

原创文章,作者:小编小本本,如若转载,请注明出处:https://www.benjiyun.com/yunzhujiyunwei/vps-yunwei/7173.html