I am working on a promise chain that was inspired by this answer:
https://stackoverflow.com/a/44955506/7485805
I want to break this for loop in order to properly handle the rejection of the chain. I just figured that I can't use break
inside the .catch
method of the chain.
Here's my code if it helps:
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
are three functions chained together in function pro
While, x
resolves successfully, y
is executed but gets rejected.
I want to stop the execution of z
since it's pointless to continue and may produce error in actual code.
Also, if someone can recommend me a better version for this piece of code:
.then(() => {
if(i === (length - 1) )
resolve();
})
Note: I cannot use await
since this code will be executed server-side and use of await
may block other incoming requests.
It is much easier to do with the async/await
syntax:
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);
});
A few things: when you construct your promise chain inside of a for loop, that's all that happens: the chain is constructed. The .then
execution will happen at the earliest in the next event loop. I'll try to illustrate:
var promiseChain = functionReturningPromise();
for(var i=0;i<3;i++){
promiseChain = promiseChain.then(x=> {
return anotherPromiseFunction(x);
});
}
Depending on what functionReturningPromise
actually does, something may have already happened at this point...or maybe not. For example we may have started a fetch
, or maybe started up a WebWorker.
However if we had a setTimeout
nested inside of the first Promise, then all we did was put the setTimeout
callback into the queue for the next cycle of the event loop. But guaranteed 100%, no .then
functions have run yet. That comes later, in the next event loop.
So next event loop comes, and the promise has resolved. That means the next .then
will get run. Let's say it fails. At that point, because we chained the promises (promiseChain = promiseChain.then
), we immediately skip to the first .catch
(or .then
with a second parameter) in the chain, and all intervening .then
s get completely skipped without being executed. Or if there are no catch
es, then this promise chain is done. No break necessary; it's just how Promises work.
So if you just add a .catch
at the very end of the chain, you're good.
About this "event loop" thing: I really recommend watching Jake Archibald: In The Loop, from JSConf.Asia 2018.
Also about await
...Sounds as if there is some confusion about how it works. You can only use await
inside an async
function, so you can never completely block thread execution with a single await
. It works just like chaining .then
s, just syntactic sugar. So @trincot is definitely right, you're going to be much happier with that syntax.
Here's an alternative answer that just executes the promises in succession unless a promise was rejected which is then stopped. This doesn't use await but it should give you a general idea how it could be done without it but this code was also written very quickly so it's not the most optimised code.
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);