我正在尝试重试JS中的一些异步调用。当简化并重写为setTimeout时,逻辑如下所示:



let error = true

let promise = null

const runPromise = (value) => new Promise((res) => {
    if (!error) {
        res()
        return
    }

    if (promise) {
        return promise.then(() => {
            return runPromise(value)
        })
    }

    promise = new Promise((res2) => {
        setTimeout(() => {
            promise = null
            console.log(value)
            error = false
            res2()
        }, 1000)
    }).then(() => res())
})

runPromise(1).then(() => { console.log(1) })
runPromise(2).then(() => { console.log(2) })
runPromise(3).then(() => { console.log(3) })





为什么从来没有调用thenrunPromise(2)runPromise(3)块?

最佳答案

您的问题是在if (promise)情况下,runPromise返回的承诺永远不会被res()兑现。从执行程序回调中return执行任何操作。您可以通过执行此操作来解决此问题

const runPromise = (value) => new Promise((res) => {
    if (!error) {
        console.log("resolve immediately without error")
        res()
    } else if (promise) {
        promise.then(() => {
            console.log("resolve after waiting for previous promise")
            res(runPromise(value))
        })
    } else {
        promise = new Promise((res2) => {
            setTimeout(() => {
                promise = null
                error = false
                res2()
                console.log("resolve after timeout")
                res()
            }, 1000)
        })
    }
})


但实际上您应该避免使用Promise constructor antipattern,它首先会导致此错误。不要在外部then执行器中调用new PromiserunPromise()new Promise!改为使用



let error = true
let promise = null

function runPromise(value) {
    if (!error) {
        console.log(value, "resolve immediately without error")
        return Promise.resolve();
    } else if (promise) {
        console.log(value, "defer until promise")
        // now this `return` works as expected
        return promise.then(() => {
            console.log(value, "trying again")
            return runPromise(value)
        })
    } else {
        console.log(value, "starting timeout")
        promise = new Promise(res2 => {
            setTimeout(res2, 1000)
        }).then(() => {
            promise = null
            error = false
            console.log(value, "waited for timeout")
        });
        return promise;
    }
}

runPromise(1).then(() => { console.log(1) })
runPromise(2).then(() => { console.log(2) })
runPromise(3).then(() => { console.log(3) })

10-07 20:44