在 for 循环中打破 Promise 链

Break a Promise chain inside for-loop

我正在研究受此答案启发的承诺链:

我想打破这个for循环,以便正确处理拒绝链。我只是想我不能在链的 .catch 方法中使用 break

如果有帮助,这是我的代码:

function pro (arr) {
  let chain = Promise.resolve();
  const self = {req: {}, res: {}};
  const length = arr.length;

  return new Promise((resolve, reject) => {
    for(let i=0; i<length; i++){
      chain = chain
          .then(() => arr[i].call(self) )
          .then(() => {
            if(i === (length - 1) )
              resolve();
          })
          .catch(e => {
            reject(e);
          })
    }
  })
  .then(() => {
    return self
  })
  .catch(e => {
    throw new Error (e);
  })

}

const x = function () {
  const self = this;
  return new Promise(resolve => {
    self.req = {key: "value"}
    resolve();
  })  
}

const y =  function () {
  const self = this;
  return new Promise((resolve, reject) => {
    console.log(self);
    reject();
  })
}

const z = function () {
  const self = this;
  return new Promise((resolve, reject) => {
    console.log('failed');
  })
}



pro([x, y, z])
.then((self) => {
  console.log('final',self);
})
.catch(e => {
  console.log('error', e);
})

x, y, z 是在函数 pro 中链接在一起的三个函数 x 解析成功,y 执行但被拒绝。

我想停止 z 的执行,因为继续执行没有意义,并且可能会在实际代码中产生错误。

此外,如果有人可以为我推荐这段代码的更好版本:

.then(() => {
  if(i === (length - 1) )
    resolve();
})

注意:我不能使用 await,因为此代码将在服务器端执行,使用 await 可能会阻止其他传入请求。

使用 async/await 语法更容易:

async function pro(arr) {
    const self = {req: {}, res: {}};
    for(const f of arr) await f.call(self);
    return self;
}

async function pro(arr) {
    const self = {req: {}, res: {}};
    for(const f of arr) await f.call(self);
    return self;
}

const x = function () {
  const self = this;
  return new Promise(resolve => {
    self.req = {key: "value"}
    resolve();
  })  
}

const y =  function () {
  const self = this;
  return new Promise((resolve, reject) => {
    console.log(self);
    reject("y failed");
  })
}

const z = function () {
  const self = this;
  return new Promise((resolve, reject) => {
   console.log('failed');
  })
}

pro([x, y, z]).then((self) => {
  console.log('final',self);
})
.catch(e => {
  console.log('error', e);
});

一些事情:当你在 for 循环中构建你的承诺链时,这就是所有发生的事情:链被构建。 .then 最早会在下一个事件循环中执行。我将尝试说明:

var promiseChain = functionReturningPromise();
for(var i=0;i<3;i++){
  promiseChain = promiseChain.then(x=> {
    return anotherPromiseFunction(x);
  });
}

根据 functionReturningPromise 实际执行的操作,此时可能已经发生了某些事情……也可能没有。例如,我们可能已经启动了一个 fetch,或者可能启动了一个 WebWorker.,但是如果我们在第一个 Promise 中嵌套了一个 setTimeout,那么我们所做的就是将 [=15] =]回调进入事件循环的下一个周期的队列。但保证 100%,还没有 .then 个函数有 运行。稍后,在下一个事件循环中。

所以下一个事件循环来了,承诺已经解决。这意味着下一个 .then 将获得 运行。假设它失败了。在那一点上,因为我们链接了承诺(promiseChain = promiseChain.then),我们立即跳到链中的第一个 .catch(或带有第二个参数的 .then),所有中间的 .thens 被完全跳过而没有被执行。或者如果没有 catches,那么这个 promise 链就完成了。无需休息;这就是 Promise 的工作方式。

因此,如果您只是在链的最后添加一个 .catch,就可以了。

关于这个 "event loop" 事情:我 真的 推荐观看 Jake Archibald: In The Loop,从 JSConf.Asia 2018 年开始。

还有关于 await...听起来好像对它的工作原理有些困惑。您只能在 中使用 await 一个 async 函数,因此您永远无法用一个 await 完全阻止线程执行。它就像链接 .then 一样工作,只是语法糖。所以@trincot 绝对是正确的,你会更喜欢这种语法。

这是一个替代答案,它只是连续执行承诺,除非承诺被拒绝然后停止。这不使用 await 但它应该让您大致了解如何在没有它的情况下完成但是这段代码也写得非常快所以它不是最优化的代码。

const x = function() {
  return new Promise(resolve => {
    resolve('it resolved ma!');
  });
};

const y = function() {
  const self = this;
  return new Promise((resolve, reject) => {
    reject("reject");
  });
};

const z = function() {
  const self = this;
  return new Promise((resolve, reject) => {
    resolve("never gets executed");
  });
};

function runPromises(promises) {
  const results = [];
  let count = 0;
  const executePromise = i => {
    count++;
    return promises[i]()
      .then((response) => {
        results.push(response);
        if (count !== promises.length) {
          executePromise(count);
        }
      })
      .catch((e) => {
        results.push(e);
        console.log("stop right now, thank you very much");
      });
  };
  if (Array.isArray(promises)) {
    executePromise(count);
  }
  return results;
}

const results = runPromises([x, y, z]);
console.log(results);