连续触发异步之后的处理方式

求解惑:连续点击下面的按钮,触发异步请求,下面三种方法处理,为什么使用方法三之后,不能得到正确的值


<!DOCTYPE html>
<html lang="en">

<head>

</head>

<body>
    <button onclick="fn()">点击我</button>
    <input type="text" id="inp">
</body>
<script>
    const query = () => {
        return new Promise((succ, fail) => {
            setTimeout(() => {
                succ(1)
            }, 1000);
        })
    }
    let cont = 1
    let fn = async () => {
        //方法一:可以
        // query().then(res => {
        //     cont += res
        //     inp.value = cont
        // })

        //方法二:可以
        // let num = await query()
        // cont += num

        //方法三:不行,求解答
        cont += await query()
        inp.value = cont
    }
</script>

</html>

参考GPT和自己的思路:

方法三无法得到正确的值的原因是因为在async函数内部调用了await关键字,它会使得函数内部的代码被挂起,并等待Promise对象的结果,然而在此代码中,每次点击按钮都会生成一个新的Promise对象,在第二个Promise对象完成之前,第一个Promise对象并没有完成,所以在第一个Promise对象完成之后,cont以前的值已经被累加了两次,导致了结果的错误。更改方法可以使用async和await组合操作或者生成执行上下文环境。

该回答引用GPTᴼᴾᴱᴺᴬᴵ
在方法三中,cont += await query()会先执行异步操作query()并返回一个Promise对象,然后将该Promise对象的resolve值加到cont上。但是在执行异步操作期间,下一行代码inp.value = cont已经开始执行了,此时cont的值还没有被更新。因此,在执行inp.value = cont时,cont的值仍然是1,而不是2。这就是为什么方法三不能得到正确的值的原因。
·
要解决这个问题,可以将异步操作和后续的代码分别放在两个then()回调函数中,以确保异步操作完成后再执行后续代码。例如,可以将方法三改写为:

let fn = () => {
    query().then(res => {
        cont += res
    }).then(() => {
        inp.value = cont
    })
}


这样就能保证异步操作完成后,cont的值已经被正确更新,然后再将更新后的值赋给inp.value。