使用工作人员时如何停止Promise循环

时间:2019-07-26 15:38:32

标签: javascript promise

let stop = false;

function functionA() {
    return Promise.reject();
}
const processMe = function(label) {
    if (stop) {
      console.log("processMe stop");
      return;
    }
    functionA()
    .then(function() {
        console.log(`${label} then handler`);
    })
    .catch(function(err) {
        stop = true;
        console.log(`${label} catch handler`);
    });
};

console.log("Starting loop");
for (let n = 0; n < 10; ++n) {
  if (stop) {
    break;
  }
  setTimeout(function(){ 
            processMe(n)
        }, n * 1000);
}
console.log("Loop complete");

以上代码并非100%完成。如果它像现在那样运行,则可以正常工作,问题在于当functionA执行其中调用Promise.reject()的Worker时,运行此代码将导致:

Starting loop
Loop complete
0 catch handler
1 catch handler
2 catch handler
3 catch handler
4 catch handler
...

因此使用“停止”标志将无效。

1 个答案:

答案 0 :(得分:1)

Chrome上的行为与其他任何符合规范的JavaScript引擎相同:如果functionAprocessMe的承诺在该循环中始终被拒绝,则catch处理程序将循环完成后,每次添加到其中的代码都将称为 。保证回调将异步发生。¹如果functionA 自身引发异常,因为它是同步的(假定它不在async函数中),则该异常将同步终止循环。但不是诺言拒绝。

示例:

function functionA() {
    return Promise.reject();
}
const processMe = function(label) {
    functionA()
    .then(function() {
        console.log(`${label} then handler`);
    })
    .catch(function(err) {
        console.log(`${label} catch handler`);
    });
};

console.log("Starting loop");
for (let n = 0; n < 10; ++n) {
  processMe(n);
}
console.log("Loop complete");

输出为:

Starting loop
Loop complete
0 catch handler
1 catch handler
2 catch handler

...等等,在Chrome,Firefox,Edge和任何符合规范的引擎上。


您在评论中说过,您只希望触发一个catch。为此,可以:

  • 如果希望异步操作并行运行,请使用Promise.all,并让processMe返回承诺链(而不使用catch)。然后在循环所在的位置使用catch
  • 如果您希望异步调用以串行方式(一个接一个)而不是并行地进行,则将彼此的承诺彼此束缚。

同时进行-请注意,我给了最后一个失败的机会为50%,因此请运行几次以查看成功和错误:

function functionA(n) {
    console.log(`functionA starting ${n}`)
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (n === 9 && Math.random() < 0.5) {
                reject(new Error(`error processing ${n}`));
            } else {
                resolve();
            }
        }, Math.random() * 1000);
    });
}
const processMe = function(n) {
    return functionA(n)
    .then(result => {
        console.log(`${n} then handler`);
        return result;
    });
};

console.log("Starting loop");
const promises = [];
for (let n = 0; n < 10; ++n) {
  promises.push(processMe(n));
}
Promise.all(promises)
.then(() => {
    console.log("All promises completed");
})
.catch(error => {
    console.log(`Got rejection: ${error.message}`);
});
.as-console-wrapper {
    max-height: 100% !important;
}

连续(第2个,共1个)(最后一个失败的几率为50%)

function functionA(n) {
    console.log(`functionA starting ${n}`)
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (n === 9 && Math.random() < 0.5) {
                reject(new Error(`error processing ${n}`));
            } else {
                resolve();
            }
        }, Math.random() * 200);
    });
}
const processMe = function(n) {
    return functionA(n)
    .then(result => {
        console.log(`${n} then handler`);
        return result;
    });
};

console.log("Starting loop");
let promise = Promise.resolve();
for (let n = 0; n < 10; ++n) {
  promise = promise.then(() => processMe(n));
}
promise
.then(() => {
    console.log("All promises completed");
})
.catch(error => {
    console.log(`Got rejection: ${error.message}`);
});
.as-console-wrapper {
    max-height: 100% !important;
}

串联(第2个,共2个):如果循环正在遍历数组,并且您想串联进行,则有一个成语,即“ promise reduce”成语。看起来像这样(上一次失败的几率是50%):

function functionA(n) {
    console.log(`functionA starting ${n}`)
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (n === 9 && Math.random() < 0.5) {
                reject(new Error(`error processing ${n}`));
            } else {
                resolve();
            }
        }, Math.random() * 200);
    });
}
const processMe = function(n) {
    return functionA(n)
    .then(result => {
        console.log(`${n} then handler`);
        return result;
    });
};

console.log("Starting loop");
const theArray = [0,1,2,3,4,5,6,7,8,9];
const promise = theArray.reduce((p, n) => {
    return p.then(() => processMe(n));
}, Promise.resolve());
promise
.then(() => {
    console.log("All promises completed");
})
.catch(error => {
    console.log(`Got rejection: ${error.message}`);
});
.as-console-wrapper {
    max-height: 100% !important;
}


¹承诺完成次数为queued to the PromiseJobs queue。由于JavaScript具有从运行到完成的语义,因此直到当前作业(运行循环)完成之前,才能运行PromiseJobs队列中的作业。