【问题标题】:Can't reach value in async function with setInterval - JavaScript无法使用 setInterval 在异步函数中达到值 - JavaScript
【发布时间】:2021-08-25 15:57:34
【问题描述】:

我正在尝试使用按钮开始自动拉卡,并在 croupierCount 达到 17 时停止它,使用 setIntervalcroupierCount 的值在下面使用此函数更改(<div> 显示此计数),但是当我试图在函数内达到此值以停止间隔时,它的记录值为 0。你能帮我解决这个问题吗?

const TheGame = () => {

  const [croupierCount, setCroupierCount] = useState(0);
  const [croupierHand, setCroupierHand] = useState([]);

  const onStandHandler = () => {            // triggered with button

     const croupierInterval = async () => {
        
        let card = await fetchCard(deck);  // fetching new card with every iteration (works) 
        croupierHand.push(card);  // pushes fetched card (works)
    
        if (card[0].value === 'ACE') {
            setCroupierCount((prevCount) => prevCount + 11);
        }
    
        else if (card[0].value === 'JACK' || card[0].value === 'QUEEN' || card[0].value === 'KING') {
            setCroupierCount((prevCount) => prevCount + 10);
        }
        else {
            setCroupierCount((prevCount) => prevCount + Number(card[0].value));
        };         
                                                         
        // croupierCount is changing (I'm displaying it in div)

        console.log(croupierCount); // croupierCount = 0, I don't know why.

        if(croupierCount > 17) {
            clearInterval(startInterval);
        };
    }

    const startInterval = setInterval(croupierInterval, 1000);

  };
};

【问题讨论】:

  • 您正在使用croupierCount,就好像它是一个全局变量一样。是吗?缺少调试问题的关键代码。赋予croupierCount 其价值的代码在哪里?
  • 抱歉,我剪错了片段。现在它已编辑@KIKOSoftware。
  • 什么会触发 onStandHandler?
  • @raina77ow - 按钮
  • 不应该是setCroupierCount(croupierCount + 11);吗? reactjs.org/docs/hooks-state.html

标签: javascript reactjs async-await setinterval


【解决方案1】:

你似乎错过了一个重要的点:在这里使用const...

const [croupierCount, setCroupierCount] = useState(0);

... 使croupierCount 成为一个常量 值,无论setCroupierCount 被调用多少次。这个变量不能直接更新:你看到的更新实际上是 React 内部状态的变化,在组件重新渲染时用相同的名称表示 - 并且再次调用渲染函数。

这种不变性对于基于钩子的功能组件来说既是福也是祸。


这里发生了什么:

  • 当组件被渲染时,TheGame 函数第一次被调用。它的 useState 调用将 value 和相应的 setter 都初始化为与该组件实例相关的内部 React 状态的一部分。

这些值从 useState 函数返回 - 并存储在 TheGame 函数、croupierCountsetCroupierCount 的局部变量(常量!)中。重要但经常被忽略的是这些特定变量每次调用TheGame 函数时都会重新创建

  • 然后创建onStandHandler 函数,将上述两个局部变量都作为其作用域的一部分。

  • 在某些时候,onStandHandler 函数被触发(当用户按下按钮时)。它创建了另一个函数croupierInterval,它应该首先获取数据,然后通过调用setCroupierCount 来更新状态并使用此获取的结果。

不过,这个函数有两个问题。

首先,croupierInterval 看到的是 当前 croupierCountsetCroupierCount 变量的值。它不能神奇地“窥视”这些变量在触发重新渲染和下次执行 TheGame 函数时将携带的值 - 因为这些实际上是新变量!

但是您似乎忽略了一个更大的问题:setInterval 不能很好地与fetch(或任何异步操作)配合使用。无需等待该动作的处理,您只需让 JS 定期触发该函数即可。

这不仅会破坏预期的延迟(放慢 fetch 使其需要 10 秒,然后看看会发生什么),而且这里有一个实际的错误:因为 clearInterval(startInterval) 不会停止处理所有部分await fetchCard(deck) 之后的函数,在最坏的情况下,您的荷官可能会超过 17。


这是“为什么”部分,但“如何解决”是什么?这里有几件事值得尝试:

  • 避免像瘟疫一样在功能组件中使用 setInterval:通常有更好的替代品。特别是在这种情况下,您至少应该将设置调用 croupierInterval 绑定到上一次调用的完成

  • useEffect 每当您希望通过某种方式间接修改您的状态作为某种副作用时。这不仅使您的代码更易于阅读和理解,还可以让您清除副作用的副作用(如超时/间隔设置)

  • 不要忘记处理人为错误:如果用户错误地双击此按钮会发生什么?

【讨论】:

  • 感谢您提供的信息。是否推荐任何其他方法来解决此问题?
  • 至少,去掉interval,在第一个循环结束的时候调用fetcher函数。一般来说,考虑使用useEffect钩子来触发这样的事情;如果用户离开并且不再需要该操作,这将允许您取消延迟的操作。
【解决方案2】:

如果有人遇到类似问题,我将发布我的解决方案。

const isMount = useRef(false);

...

useEffect(() => {
  
    if (isMount.current && croupierCount !== 0) {
        
        if (croupierCount < 17) {
            setTimeout(() => {
                onStandHandler();
            }, 1000);
        }
    }

    else isMount.current = true;
}, [croupierCount]);

const onStandHandler = async () => {

        let card = await fetchCard(deck, 1);
        croupierHand.push(card);
    
        if (card[0].value === 'ACE') {
            setCroupierCount(prevCount => prevCount + 11);
        }
    
        else if (card[0].value === 'JACK' || card[0].value === 'QUEEN' || card[0].value === 'KING') {
            setCroupierCount(prevCount => prevCount + 10);
        }
        else {
            setCroupierCount(prevCount => prevCount + Number(card[0].value));
        };    
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-29
    • 2021-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多