我怎样才能打破这个 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);
})
编辑: 我知道如何使用 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);
})