那么block不会在链接的promise中被调用

时间:2019-08-15 12:05:45

标签: javascript es6-promise

我正在尝试重试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)的{​​{1}}块从未被调用?

2 个答案:

答案 0 :(得分:2)

您的问题是,在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) })

答案 1 :(得分:0)

您或多或少在这里有无限的递归。首次设置promise =后,if(promise)将始终进入,并将始终附加另一个.then回调,该回调在被调用时将一次又一次地执行相同的功能。它也永远不会调用res()

在这里,我通常会单独提出一些问题:让一个函数建立promise链,并让它接受一个任务函数,并使用提供的值来调用它:

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