无法使用 setInterval 在异步函数中获取值 - JavaScript
Can't reach value in async function with setInterval - JavaScript
我正在尝试通过按钮开始自动拉取卡片,并在 croupierCount
达到 17 时停止,使用 setInterval
。 croupierCount
的值随着下面的这个函数发生变化(<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);
};
};
您似乎忽略了一个重点:在这里使用 const
...
const [croupierCount, setCroupierCount] = useState(0);
... 使 croupierCount
成为 常数 值,无论调用 setCroupierCount
多少次。这个变量不能直接更新:你看到的它的更新实际上是 React 内部状态的变化,当组件被重新渲染时用相同的名称表示 - 渲染函数被再次调用。
这种不变性对于基于钩子的函数式组件来说既是福也是祸。
这是这里发生的事情:
- 渲染组件时,
TheGame
函数被第一次调用。它的 useState
调用初始化值和相应的 setter 作为绑定到该组件实例的内部 React 状态的一部分。
这些值从 useState
函数返回 - 并存储在 TheGame
函数、croupierCount
和 setCroupierCount
的局部变量(常量!)中。重要的 - 并且经常被遗漏的 - 是这些特定变量会在每次 TheGame
函数被调用时重新创建 !
然后 onStandHandler
函数被创建,上述两个局部变量都作为其范围的一部分可用。
在某些时候,onStandHandler
函数被触发(当用户按下按钮时)。它创建了另一个函数,croupierInterval
,它应该首先获取数据,然后通过调用 setCroupierCount
来更新状态。
不过这个函数有两个问题。
首先,所有 croupierInterval
看到的是 current croupierCount
和 setCroupierCount
变量的值。当重新渲染被触发并且 TheGame
函数下次执行时,它不能神奇地 'peek' 这些变量将携带哪些值 - 因为这些实际上将是新变量!
但是您似乎忽略了一个更大的问题:setInterval
不能很好地与 fetch
(或任何异步操作)配合使用。不用等待那个action的处理,你只要让JS周期性的触发这个函数即可。
这不仅会造成预期的延迟(减慢 fetch
使其需要 10 秒,然后看看会发生什么),而且这里还有一个实际的错误:因为 clearInterval(startInterval)
没有不要停止处理 await fetchCard(deck)
之后的函数的所有部分,在最坏的情况下,您的荷官可能会超过 17。
这是 'why' 的部分,但是 'how to fix' 的内容是什么?这里有几件事值得尝试:
避免像瘟疫一样在功能组件中使用 setInterval:通常有更好的替代品。特别是在这种情况下,您至少应该将设置调用 croupierInterval
与上一个调用的完成
联系起来
useEffect
每当你想要一些东西作为某种副作用间接修改你的状态时。这不仅使您的代码更易于阅读和理解,而且还可以让您清除副作用的副作用(如 timeouts/intervals 集)
也不要忘记处理人为错误:如果用户错误地双击此按钮会发生什么?
如果有人遇到类似问题,我会发布我的解决方案。
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));
};
};
我正在尝试通过按钮开始自动拉取卡片,并在 croupierCount
达到 17 时停止,使用 setInterval
。 croupierCount
的值随着下面的这个函数发生变化(<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);
};
};
您似乎忽略了一个重点:在这里使用 const
...
const [croupierCount, setCroupierCount] = useState(0);
... 使 croupierCount
成为 常数 值,无论调用 setCroupierCount
多少次。这个变量不能直接更新:你看到的它的更新实际上是 React 内部状态的变化,当组件被重新渲染时用相同的名称表示 - 渲染函数被再次调用。
这种不变性对于基于钩子的函数式组件来说既是福也是祸。
这是这里发生的事情:
- 渲染组件时,
TheGame
函数被第一次调用。它的useState
调用初始化值和相应的 setter 作为绑定到该组件实例的内部 React 状态的一部分。
这些值从 useState
函数返回 - 并存储在 TheGame
函数、croupierCount
和 setCroupierCount
的局部变量(常量!)中。重要的 - 并且经常被遗漏的 - 是这些特定变量会在每次 TheGame
函数被调用时重新创建 !
然后
onStandHandler
函数被创建,上述两个局部变量都作为其范围的一部分可用。在某些时候,
onStandHandler
函数被触发(当用户按下按钮时)。它创建了另一个函数,croupierInterval
,它应该首先获取数据,然后通过调用setCroupierCount
来更新状态。
不过这个函数有两个问题。
首先,所有 croupierInterval
看到的是 current croupierCount
和 setCroupierCount
变量的值。当重新渲染被触发并且 TheGame
函数下次执行时,它不能神奇地 'peek' 这些变量将携带哪些值 - 因为这些实际上将是新变量!
但是您似乎忽略了一个更大的问题:setInterval
不能很好地与 fetch
(或任何异步操作)配合使用。不用等待那个action的处理,你只要让JS周期性的触发这个函数即可。
这不仅会造成预期的延迟(减慢 fetch
使其需要 10 秒,然后看看会发生什么),而且这里还有一个实际的错误:因为 clearInterval(startInterval)
没有不要停止处理 await fetchCard(deck)
之后的函数的所有部分,在最坏的情况下,您的荷官可能会超过 17。
这是 'why' 的部分,但是 'how to fix' 的内容是什么?这里有几件事值得尝试:
避免像瘟疫一样在功能组件中使用 setInterval:通常有更好的替代品。特别是在这种情况下,您至少应该将设置调用
联系起来croupierInterval
与上一个调用的完成useEffect
每当你想要一些东西作为某种副作用间接修改你的状态时。这不仅使您的代码更易于阅读和理解,而且还可以让您清除副作用的副作用(如 timeouts/intervals 集)也不要忘记处理人为错误:如果用户错误地双击此按钮会发生什么?
如果有人遇到类似问题,我会发布我的解决方案。
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));
};
};