那么 block 不会在链式承诺中被调用

then block isn't get called in chained promises

我正在尝试在 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,它首先导致了这个错误。不要在外部 new Promise 执行器中调用 thennew PromiserunPromise()!而是使用

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) })

你这里或多或少有无穷无尽的递归。一旦你第一次设置了 promise =if(promise) 总是会进入,它总是会附加另一个 .then 回调,当被调用时,只是一次又一次地执行相同的函数。它也永远不会调用 res().

我通常会在这里分开关注:让一个函数构建承诺链并让它接受一个任务函数,该函数使用提供的值进行调用:

 let queue = Promise.resolve(); // no need for null, let it always be a promise

 function runTask(task, ...args) {
  return queue = queue.then(() => task(...args));
 }

然后写另一个函数,代表一个任务,例如在你的例子中,它是一个等待滴答的函数:

 function waitTask(n) { return new Promise(res => setTimeout(res, 1000, n)); }

runTask(waitTask, 1);
runTask(waitTask, 2);

let queue = Promise.resolve();

function runTask(task, ...args) {
  return queue = queue.then(() => task(...args));
}


function waitTask(n) { return new Promise(res => setTimeout(res, 1000, n)); }

runTask(waitTask, 1).then(console.log);
runTask(waitTask, 2).then(console.log);