JavaScript:递归函数无法正确处理Promises



我正在Node中编写一个简单的游戏。球员做出了一个动作,这是一个承诺。接下来,对此举的合法性进行检查:

  1. 如果合法-伟大,执行下一个"。那么">
  2. 如果不是-递归调用相同的函数以获得新的移动

什么是有效的:我设法使用";拒绝"+"捕获";并递归地调用该函数。

不起作用的是:当第二次调用_makeMove时,它应该向玩家请求新的移动,暂停直到他们回复。实际发生的情况是,函数只运行到步骤3,不等待玩家的进入

我正在通过终端接受输入,窗口仍然处于"0";请输入:";,但是代码已经跑了。。(最终打出了一个"未定义"的错误,这是很自然的,因为玩家还没有机会进入新的动作(。

代码(简化(:

const _makeMove = (activePlayer) => {
//player makes a move, which is returned as a promise
activePlayer.proposeMove()
//Step 1 - check if legal
.then(proposedMove => {
if (!gameBoard.checkLegal(proposedMove)) {
return Promise.reject("bad entry");
}
return proposedMove;
})
//Step 2 - record the move
.then(proposedMove => {
activePlayer.recordMove(proposedMove);
return proposedMove;
}) //unless step 1 fails...
.catch(err => {
console.log('oops bad entry!')
//in which case let's ask the player to move again...
//by calling the function RECURSIVELY
_makeMove(activePlayer);
})
//Step 3 - game goes on...
.then(proposedMove => {
//more stuff
})
}

我完全感到困惑。为什么递归调用没有按预期工作?

为了让您的承诺链从_makeMove获取值,您必须返回_makeMove的结果。不要担心承诺的价值;它将在调用链中的下一个CCD_ 4之前被自动解析。

.catch(err => {
console.log('oops bad entry!')
return _makeMove(activePlayer);
//  ^ return here
})

但是,这里的_makeMove结果将在返回then之前完成,这可能会使您的recordMove调用返回两次。您可能需要拆分为_makeMove_recordMove函数,这样对_makeMove的递归调用就不会记录移动。

尽管理论上可能会耗尽堆栈,但对于合理数量的移动尝试,它不会影响正确性。上面的两个错误会。

我不确定您是否应该在这里使用递归调用。如果你有一个非常转储的播放器,你可能会出现堆栈溢出错误;(

const _makeMove = async (activePlayer) => {
let proposedMove = null;
while (1) {
proposedMove = await activePlayer.proposeMove();
if (gameBoard.checkLegal(proposedMove)) {
break;
}
}
activePlayer.recordMove(proposedMove);
// do more stuff
}

感谢您的建议。经过修补,我最终做了两件事来解决它:

  1. 我使用async/await从一堆.then方法重写了我的承诺链。这解决了递归调用进入第3步而不在第2步暂停的最初问题(我仍然觉得这很神奇。(
  2. 然而,这就产生了一个新的问题。最初,我将一个.catch链接到async函数的末尾,但这只触发了一次。即第二、第三、第四次递归调用将导致Unhandled exception错误。因此,我将所有代码放在函数内的try块内,并将catch也放在函数内。这个效果很好

最终代码:

const _makeMove = async (activePlayer) => {
try {
//Step 1 - take input
const proposedMove = await activePlayer.proposeMove();
//Step 2 - check if legal
if (!gameBoard.checkLegal(proposedMove)) {
throw new Error("bad entry");
}
//Step 3 - etc
//Step 4 - etc
//Step 5 - etc
} catch (e) {
console.log('oops bad entry!')
console.log("how it works: type 2 numbers ONLY, each between 1 and 3 (no spaces), to signify your move")
console.log("eg to place a mark into bottom left corner type 33. First cell = 11. Bang in the center = 22. You get it.")
console.log('lets try again...')
return _makeMove(activePlayer);
}
};

最新更新