文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

useEffect中不能使用async的原理是什么

2023-07-02 16:46

关注

本篇内容介绍了“useEffect中不能使用async的原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

当页面中使用 useEffect 的时候,会在初始化的时候执行 mountEffect 如下:

useEffect: function(create, deps) {  currentHookNameInDev = "useEffect";  mountHookTypesDev();  checkDepsAreArrayDev(deps);  return mountEffect(create, deps);},

执行 mountEffect 的时候执行 mountEffectImpl 如下:

function mountEffectImpl(fiberFlags, hookFlags, create, deps) {  var hook = mountWorkInProgressHook();  var nextDeps = deps === void 0 ? null : deps;  currentlyRenderingFiber$1.flags |= fiberFlags;  hook.memoizedState = pushEffect(HasEffect | hookFlags, create, void 0, nextDeps);}

之后执行 pushEffect,在 pushEffect 中会创建一个 effect 节点,然后添加到当前函数对应 fiber 的 updateQueue 上面,数据结构是一个环链。

function pushEffect(tag, create, destroy, deps) {  var effect = {    tag,    create,    destroy,    deps,    next: null  };  var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;  if (componentUpdateQueue === null) {    componentUpdateQueue = createFunctionComponentUpdateQueue();    currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;    componentUpdateQueue.lastEffect = effect.next = effect;  } else {    var lastEffect = componentUpdateQueue.lastEffect;    if (lastEffect === null) {      componentUpdateQueue.lastEffect = effect.next = effect;    } else {      var firstEffect = lastEffect.next;      lastEffect.next = effect;      effect.next = firstEffect;      componentUpdateQueue.lastEffect = effect;    }  }  return effect;}

中间又是一大堆调度,协调的逻辑,不是我们关注的重点,这里省略掉直接进入到 schedulePassiveEffects,这个函数作用是从函数组件对应的 fiber 上获取上面挂载的 effect,然后将 effect 和 fiber 堆到 pendingPassiveHookEffectsUnmount 和 pendingPassiveHookEffectsMount 这个两个队列中

function schedulePassiveEffects(finishedWork) {  var updateQueue = finishedWork.updateQueue;  var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;  if (lastEffect !== null) {    var firstEffect = lastEffect.next;    var effect = firstEffect;    do {      var _effect = effect      , next = _effect.next      , tag = _effect.tag;      if ((tag & Passive$1) !== NoFlags$1 && (tag & HasEffect) !== NoFlags$1) {        //         enqueuePendingPassiveHookEffectUnmount(finishedWork, effect);        enqueuePendingPassiveHookEffectMount(finishedWork, effect);      }      effect = next;    } while (effect !== firstEffect);  }}

这里是推入的逻辑,只展示推入挂载队列的方法,推入卸载队列是一样的

function enqueuePendingPassiveHookEffectMount(fiber, effect) {  pendingPassiveHookEffectsMount.push(effect, fiber);  if (!rootDoesHavePassiveEffects) {    rootDoesHavePassiveEffects = true;    scheduleCallback(NormalPriority$1, function() {      flushPassiveEffects();      return null;    });  }}

之后又是一大推调度,协调的逻辑,等待协调执行完毕后,之后会进入 flushPassiveEffectsImpl ,函数太长了,只贴出相关的部分,逻辑是循环挂载 effect 队列中的每一个 effect 传入到 invokePassiveEffectCreate 执行

// ...var mountEffects = pendingPassiveHookEffectsMount;pendingPassiveHookEffectsMount = [];for (var _i = 0; _i < mountEffects.length; _i += 2) {  var _effect2 = mountEffects[_i];  var _fiber = mountEffects[_i + 1];  {    setCurrentFiber(_fiber);    {      invokeGuardedCallback(null, invokePassiveEffectCreate, null, _effect2);    }    if (hasCaughtError()) {      if (!(_fiber !== null)) {        {          throw Error("Should be working on an effect.");        }      }      var _error4 = clearCaughtError();      captureCommitPhaseError(_fiber, _error4);    }    resetCurrentFiber();  }}// ...

这个函数会获取 create 并执行,然后将执行结果挂载到 destroy 上,这里的 create 就是 useEffect 中的第一个参数,从这里可以看出,如果有返回值,那么 destroy 就是第一个函数的返回值,没有就是 undefined

function invokePassiveEffectCreate(effect) {  var create = effect.create;  effect.destroy = create();}

卸载的时候会通过函数组件对应的 fiber 获取 effect 链表,然后遍历链表,获取环链上的每一个节点,如果 destroy 不是 undefined 就执行,所以如果 useEffect 第一个参数传入 async, 那么这里的 destroy 就是一个 promise 对象,对象是不能执行的,所以报错。

function commitHookEffectListUnmount(tag, finishedWork) {  var updateQueue = finishedWork.updateQueue;  var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;  if (lastEffect !== null) {    var firstEffect = lastEffect.next;    var effect = firstEffect;    do {      if ((effect.tag & tag) === tag) {        // Unmount        var destroy = effect.destroy;        effect.destroy = undefined;        if (destroy !== undefined) {          destroy();        }      }      effect = effect.next;    } while (effect !== firstEffect);  }}

既然知道了原因那么,解决方案就非常简单,直接手写一个自定义 hook,包裹一下就可以处理这个问题了,hook 实现如下。

import { useEffect } from 'react'export default function useAsyncEffect<T, U extends any[]>(  method: () => Promise<T>,  deps: U) {  useEffect(() => {    (async () => {      await method()    })()  }, deps)}

使用:

import React, { useState } from 'react'import { useAsyncEffect } from './useAsyncEffect'export default function Demo() {  const [count, setCount] = useState(0)  function fetchData(): Promise<number> {    return new Promise((resolve) => {      setTimeout(() => {        resolve(count + 1)      }, 2000)    })  }  useAsyncEffect(async () => {    const count = await fetchData()    setCount(count)  }, [fetchData])  return (    <div>{count}</div>  )}

这里其实有问题,因为返回值永远是undefined,你可以开动脑筋尝试修复一下。

“useEffect中不能使用async的原理是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯