我怎样才能打破这个 Promise for 循环?

How can I break out of this Promise for loop?

编辑: 我知道如何使用 async/await 让它工作,但不幸的是,我不能使用它,因为它没有得到广泛支持(尤其是 iOS 10、Internet Explorer 等)。所以我只需要一个承诺方式。

我对 Promises 很陌生,所以我被卡住了。我有这个 for 循环,每当 i 等于 2 时我都想跳出这个循环(例如)。我已经尝试放置 reject,但它仍然会继续放置,直到 i<10。我希望它在满足我的条件时停止。

for (let i = 0, p = Promise.resolve(); i < 10; i++) {
  p = p.then(_ => new Promise((resolve, reject) => {
    setTimeout(function() {
      console.log(i);
      if (i == 2) {
        reject("BREAK HERE");
      } else {
        resolve();
      }
    }, Math.random() * 1000)
  })).catch(err => {
    console.log(err);
  });
}

当前输出:

0
1
2
BREAK HERE
3
4
5
6
7
8
9

我希望它只是:

0
1
2
BREAK HERE

去掉catch,否则错误会被捕获并处理。要记录而不是错误,请使用 console.log:

try {

  for (let i = 0, p = Promise.resolve(); i < 10; i++) {
    p = p.then(_ => new Promise((resolve, reject) => {
      setTimeout(function() {
        console.log(i);
        if (i == 2) {
          reject(console.log("BREAK HERE"));
        } else {
          resolve();
        }
      }, Math.random() * 1000)
    }));
  }

} catch (e) {}

在任何情况下,您的代码将始终执行 10 次,因为 setTimeout 是异步操作,并且在满足您的条件 reject("BREAK HERE") 之前,promise 已经被链接了 10 次。

这是你可以做的,虽然它看起来很乱:(

for (let i = 0, p = Promise.resolve(); i < 10; i++) {
  p = p.then(data => new Promise((resolve, reject) => {
    setTimeout(function() {
      if(data && data.isRejected){
        resolve({ isRejected: true })
        return;
      }
      console.log(i);
      if (i == 2) {
        reject("BREAK HERE");
      } else {
        resolve({});
      }
    }, Math.random() * 1000)
  })).catch(err => {
    console.log(err);
    /* add this line */
    return { isRejected: true };
  });
}

从 catch 返回一个对象(而不是抛出)会将 promise 标记为已解决。更多 docs 这将为您提供所需的结果

据我了解你的问题,你想按顺序执行一个接一个的承诺。然后,您希望在第一个承诺失败后立即中止。

为了确保执行顺序,你必须在for循环中这样做,尽管你失去了并发运行时间的实际好处。此代码将 运行 依次为:

const delay = async () => new Promise((resolve) => setTimeout(() => resolve(), Math.random() * 1e3);

const myUpload = async (file) => {
    await delay();

    if (file === 2) {
        return Promise.reject(`COULD NOT UPLOAD ${file}`);
    } else {
        console.log("uploaded file", file);

        return;
    }

};

const uploadFilesSequentually = async (files) => {
    for (const file of files) {
        await myUpload(file); // this will create, and wait for the promise to finish or fail, only then the for loop continues
    }
};

如果你不关心执行顺序并且允许并发,我会这样做:

const promises = myFiles.map(myUpload); // all promises are now pending and will resolve in different orders
Promise.all(promises) // returns its own promise then resolves once all of them have succeded or is rejected as soon as one is rejected
    .then((result) => console.log("everything uploaded"))
    .catch(console.error);

Promise.all 的结果会保持结果的顺序。所以即使第三个承诺在第一个之前解决,结果也会是 [resultPromise1, resultPrimise2, resultPromise3, ... ]

现在,我最终使用了一种不同的方式来实现这一点,而不需要 Promises:

var i=0;

function fetchNext(){
    if (i==2) {
        console.log("BREAK HERE");
    } else {
        setTimeout(function () {
            console.log(i);
            i++;
            fetchNext();
        }, Math.random() * 1000);
    }
}

fetchNext();

只要不解析,序列就会停止

for (let i = 0, p = Promise.resolve(); i < 10; i++) {
  p = p.then(_ => new Promise((resolve, reject) => {
    setTimeout(function() {
      console.log("i",i);
      if (i == 2) {
        console.log("break here")
        
      } else {
        resolve();
      }
    }, Math.random() * 1000)
  })).catch(err => {
    console.log("err",err);
  });
}

但是由于此代码本身打算对异步调用进行排序,所以应该考虑这样做:

// repeated call
next(
  0/*state*/, 
  i=>i<2/*match*/, 
  i=>i+1/*increment*/,     
  asyncCallPromisefied/*promise|promise[]*/, 
  [0]/*args*/
)
.catch(console.log)


// traverse array
next(
  0/*state*/, 
  i => i < 6/*match*/, 
  i => i + 1/*increment*/,
  asyncChain()/*promise|promise[]*/, 
  [0]/*args*/,
  0/*[promise[] index]*/
)
.catch(console.log)


// helper
function next(state, match, increment, action, args, pointer = 0/**inactive**/){
  if(match(state))
  if(Array.isArray(action)){
     
     return pointer < action.length
      ? action[pointer](...args)
      .then(i => next(
        increment(state), 
        match, 
        increment, 
        action, 
        [i],
        pointer + 1
      ))
      : undefined;
  } else {
    return action(...args)
    .then(i => next(
      increment(state), 
      match, 
      increment, 
      action, 
      [i]
    ))
  }
}



// wrapper
function asyncCall(i, callback){ 
  return setTimeout(() => {
    console.log(i == 2 ? 'break here': i);
    callback(null, ++i);
  }, Math.random() * 1000)
}

function asyncCallPromisefied(i){
  return new Promise(function(resolve, reject){
    asyncCall(i, function(err, args){
      if(err){
        reject(err)
      } else {
        resolve(args)
      }
    })
  })
}

function asyncChain(){return [
  function(i){console.log("chain",i);return Promise.resolve(i)},
  function(i){console.log("chain",i);return Promise.resolve(i)},
 function(i){console.log("chain",i);return Promise.resolve(i)},
  asyncCallPromisefied,
  function(i){console.log("chain",i);return Promise.resolve(i)},
]}

去掉循环中的.catch,之后再处理

let p = Promise.resolve();
for (let i = 0; i < 10; i++) {
  p = p.then(_ => new Promise((resolve, reject) => {
    setTimeout(function() {
      console.log(i);
      if (i == 2) {
        reject("BREAK HERE");
      } else {
        resolve();
      }
    }, Math.random() * 1000)
  }));
}

p.catch(err => {
    console.log(err);
  })