I am recursively calling a function which returns a Promise, but i am noticing that the next then is called even if i am rejecting with error. Below is the relevant part of the code.
const applyFilters = (counter) => {
return new Promise((resolve, reject) => {
let filter = filters[counter];
if(filter) {
applyStep(filter).then(promiseTimeout(filter.delay), function(err) {
console.log('reject with error');
reject(err);
}).then(function(res) {
console.log('still executing after reject');
resolve(applyFilters(++counter).catch(function(err) {
reject(err);
}));
});
} else {
resolve(true);
}
});
};
const applyStep = (step) => {
if(step.step_type == 'filter') {
return worksheet.applyFilterAsync(step.name, values, 'replace');
} else if(step.step_type == 'parameter') {
return workbook.changeParameterValueAsync(`${step.name}`, value);
} else {
return Promise.resolve(true);
}
};
I am seeing on console
reject with error
still executing after reject
Is this the expected behaviour, may be I am missing something. Any help in understating this further will be really great. Thanks.
You are passing the second callback to then, which handles the error (in this case by rejecting the outer promise), and then fulfills the promise returned by the then() call with the callback return value (undefined). The next then() in the chain will be executed once that promise is fulfilled.
I could tell you how to work around this problem by using a different then/catch structure, but really you need to avoid the Promise constructor antipattern here!
function applyFilters(counter) {
if (counter >= filter.length)
return Promise.resolve(true);
const filter = filters[counter];
return applyStep(filter)
.then(promiseTimeout(filter.delay))
.then(res => applyFilters(++counter));
}
Related
I was trying to implement promise from scratch.
Question:
I wasn't sure how do I implement Finally? (I'm guessing finally will
execute after then's and the catch is invoked. (ONLY once))
If you feel any refactors can be made to my code, please feel free to suggest. This is my naive attempt to implement promises.
This is my implementation:
function Promisify(fn) {
let status = 0; // 0 = unfulfilled, 1 = resolved, 2 = rejected
let result;
let error;
let thenFns = [];
let catchFns = [];
let finallyFn = undefined;
// Public Methods.
this.then = function(fn) {
thenFns.push(fn);
doThen();
return this; // for chaining
};
this.catch = function(fn) {
catchFns.push(fn);
doCatch();
return this; // for chaining
};
// TODO: Implement finally
this.finally = function(fn) {
finallyFn = fn;
// dofinally(fn);
return this;
}
// Private Methods
function resolve(r) {
if (status) throw Error('can not resolve, already handled');
status = 1;
result = r;
doThen();
}
function reject(e) {
if (status) throw Error('can not reject, already handled');
status = 2;
error = e;
doCatch();
}
function doThen() {
if (status === 1) {
while(thenFns.length) {
thenFns.shift()(result);
}
}
}
function doCatch() {
if (status === 2) {
if (catchFns.length === 0) {
console.error('uncaught error')
}
while(catchFns.length) {
catchFns.shift()(error);
}
}
}
try {
fn(resolve, reject);
} catch (e) {
reject(e);
}
}
// ======== QUESTION: Caller ================
const demoFail = new Promisify((resolve, reject) => {
setTimeout(function() {
reject('Howdy! from DemoFail')
}, 1000);
});
demoFail
.then(val => console.log("DemoFail Then!!"))
.catch(console.error) //Should throw an error.
.then(val => console.log("Second then!"))
.catch(
(err) => {
throw new Error('error', err);
})
.finally(val => console.log("Executed Finally"))
Here are some things missing from your implementation:
.then() needs to return a new promise, not the same promise.
The return value of a .then() handlers (if it's not a promise) becomes the resolved value of the newly returned promise returned in step #1.
If the return value of a .then() handler is a promise, then the newly returned promise from step 1 doesn't resolve until the promise returned from the .then() handler resolves or rejects with that value.
When calling a .then() handler, you need try/catch around the call to the handler because if the handler throws, then that turns the promise returned into step #1 into a rejected promise with the throw error as the reject reason.
There is similar logic for all the .catch() handlers (it also returns a new promise and the return value of the handler affects the newly returned promise from step #1).
When you store .then() or .catch() callback fns, you also need to store the newly create promise that you returned separate for each callback because that promise will be affected by the return value or exception thrown in the callback.
The spec itself that covers this is fairly simple. You can read it here. And, here's an implementation of that spec.
Your simple version looks like it probably works for one level of promise like fn().then(), but it doesn't do proper chaining and doesn't catch exceptions in handlers and doesn't pay attention to return values in handlers which are all pretty fundamental behaviors of promises. Unfortunately, there is no super simple way to write promises that includes the fundamental behaviors. It just takes more code to support them.
New at promises, so feel free to be verbose.
I am writing a function "extra_promises_at_start_and_end" that returns a promise to do something.
This function may know immediately that it will fail (ie: return a promise that is rejected). Question 1: Is there something like Promise.give_me_a_rejected_promise(..) or do I have to create a promise and reject it just like my code does?
Similarly, my function "extra_promises_at_start_and_end" calls other functions that return promises. At the end of this async chaining of work, I need to some final processing. Question 2a/2b: Since my function returns a promise, I need to create a promise to do this last bit of work. Is this correct that I need to create a promise and immediately accepted or reject it? Is there a Promise.give_me_a_rejected_promise(..).
My code works as expected, just feels like I am missing something, and so generating redundant code.
Code in question:
// this is the function that may have redundant code
// see question 1 and 2
function extra_promises_at_start_and_end() {
// fake out some module scope variable that indicates if this call is allowed to proceed or not
let ok_to_proceed = Math.random() > 0.5
// this function "extra_promises_at_start_and_end returns" a promise,
// Question 1: I need to create a Promise just to reject it immediatly?
if (!ok_to_proceed) {
return new Promise((resolve, reject) => { reject("failed before starting anything") }) // feels wrong
}
// do 5 things in sequence
return another_module_promise_to_do_something(1).then(() => {
return another_module_promise_to_do_something(2)
}).then(() => {
return another_module_promise_to_do_something(3)
}).then(() => {
return another_module_promise_to_do_something(4)
}).then(() => {
return another_module_promise_to_do_something(5)
}).then(() => {
// need to do something after the above 5 tasks are done,
console.log("doing something after all 5 things are done")
// this function "extra_promises_at_start_and_end" returns a promise,
// Question 2a: I need to create a promise just to resolve it immediatly?
return new Promise((resolve, reject) => { resolve(); }) // feels wrong
}).catch((id) => {
// this function extra_promises_at_start_and_end returns a promise,
// Question 2b: I need to create one just to reject it immediatly?
return new Promise((resolve, reject) => { reject(id); }) // feels wrong
})
}
The caller of this code is expecting a promise.
// run the test
console.log("calling something that will return a promise to let me know when it's done");
extra_promises_at_start_and_end()
.then(() => {
console.log("done :)")
}).catch((id) => { console.log("failed id = " + id) })
Finally, a stub for testing my function
// pretend this is a complex task (ie: not suitable for inlining)
// done by some other module
// it returns a promise
function another_module_promise_to_do_something(id) {
console.log("starting " + id)
let P = new Promise((resolve, reject) => {
console.log(" inside promise " + id)
setTimeout(() => {
if (Math.random() > 0.1) {
console.log(" finished " + id);
resolve();
} else {
console.log(" failed " + id)
reject(id);
}
}, Math.random() * 1000)
})
return P;
}
If this is the way it is supposed to be done, then let me know and I will stop searching for the correct way to use promises.
What I learned is:
Promise.resolve(value) method returns a Promise object that is resolved with the given value. So anyone calling my function can do a then on the response.
Promise.reject(reason) method returns a Promise object that is rejected with the given reason. So any chaining will fail as needed.
Any return value in then will get encapsulated in a promise. Feels like this obscures the intent. So not using.
My new function is as follows:
function promise_something() {
// fake out some module scope variable that indicates if this call is allowed to proceed or not
let ok_to_proceed = Math.random() > 0.5
if (!ok_to_proceed) {
return Promise.reject("failed before starting anything")
}
// do 5 things in sequence
return another_module_promise_to_do_something(1).then(() => {
return another_module_promise_to_do_something(2)
}).then(() => {
return another_module_promise_to_do_something(3)
}).then(() => {
return another_module_promise_to_do_something(4)
}).then(() => {
return another_module_promise_to_do_something(5)
}).then(() => {
// need to do something after the above 5 tasks are done,
console.log("doing something after all 5 things are done")
return Promise.resolve()
}
The then() and catch() functions of a Promise return a Promise.
Your last big chunk of code is indeed redundant. If you needed to do any processing after the 5th chained invocation of then(), you could just chain another then().
Here's a more bare-bones version of your code to illustrate:
const countFromOneToFive = () => {
if (Math.random() > 0.5) {
return Promise.reject("Cosmic radiation ruined your promises. Great.");
}
return Promise.resolve([])
.then((countToFive) => {
countToFive.push(1);
return countToFive;
})
.then((countToFive) => {
countToFive.push(2);
return countToFive;
})
.then((countToFive) => {
countToFive.push(3);
return countToFive;
})
.then((countToFive) => {
countToFive.push(4);
return countToFive;
})
.then((countToFive) => {
countToFive.push(5);
return countToFive;
});
};
countFromOneToFive()
.then((countToFive) => {
countToFive.forEach((number) => console.log(number));
})
.catch((error) => {
console.log(error, "Curses!");
});
You can use Promise.reject() to just return a rejected Promise. This is handled in the catch statement in the bottom.
You can do all of your processing with as many then() invocations as you want. You can simply return after your final then() to have a Promise. From there, treat it like any promise and append then()s and catch()s as you see fit.
1: Is there something like Promise.give_me_a_rejected_promise(..) or do I have to create a promise and reject it just like my code does?
like Promise.reject(err)?
// Question 2a: I need to create a promise just to resolve it immediatly?
The point where this is written in your code is inside a promise chain. You don't need to do anything at all (in that context), to return a resolved Promise, except maybe not throwing.
This Promise will resolve to any value you return, except undefined. And if you return undefined (explicitely or implicitely), this Promise will resolve to the last value before that, in the Promise chain.
Only if you need to explicitely resolve to undefined you'd have to return Promise.resolve().
// Question 2b: I need to create one just to reject it immediatly?
Still inside the Promise chain: Since a rejected value is like a thrown Error in sync code, all you need to do here is to throw.
But that is pointless in the context you asked. Why catching an Error, just to immediately throw the very same Error, without doing anything else in that catch-block.
So your code could be like
function extra_promises_at_start_and_end() {
// fake out some module scope variable that indicates if this call is allowed to proceed or not
let ok_to_proceed = Math.random() > 0.5
if (!ok_to_proceed) {
return Promise.reject("failed before starting anything");
}
// do 5 things in sequence
return another_module_promise_to_do_something(1)
.then(() => another_module_promise_to_do_something(2))
.then(() => another_module_promise_to_do_something(3))
.then(() => another_module_promise_to_do_something(4))
.then(() => another_module_promise_to_do_something(5))
.then(() => {
// need to do something after the above 5 tasks are done,
console.log("doing something after all 5 things are done")
return null; //so the previous value is no longer propagated by this Promise
})
//.catch(id => { throw id }) //is pointless.
}
When you say "This Promise will resolve to any value you return". So returning new Promise((resolve, reject) => { resolve 100}).then .... blaw blaw blaw is the same as 100.then .. blaw blaw blaw ?
Not exactly. Inside a Promise chain, returning a plain value like 100 or a Promise that resolves to 100 (like return Promise.resolve(100)) is equivalent in result ...
var foo1 = somePromise.then(() => {
//do something
return 100
})
//is equivalent in result to
var foo2 = somePromise.then(() => {
//do something
return Promise.resolve(100);
})
//or to
var foo3 = somePromise.then(() => {
//do something
return new Promise(resolve => resolve(100));
})
foo1, foo2, foo3 are all promises that resolve to the value 100 after somePromise has finished. That's equivalent in result.
... but you can't call then() on a number.
//you can do
somePromise.then(() => {
//do something
return 100
}).then(...)
//or sometimes you want to do
somePromise.then((value) => {
//usually because you need `value` inside `.then(...)`
return somethingAsync().then(...)
})
//but you can NOT do
somePromise.then(() => {
//do something
return 100.then(...)
})
There seems something inherently wrong with having to define a Promise's callback as asynchronous:
return new Promise(async (resolve, reject) => {
const value = await somethingAsynchronous();
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
});
This is apparently an antipattern and there are coding problems which can arise from it. I understand that it becomes easier to fail to catch errors here, even when placing await statements inside try/catch blocks.
My first question is, what's the best way to code something like this, when one wants to forward a Promise with different resolve/reject values? With then/catch? I.e.
return new Promise((resolve, reject) => {
somethingAsynchronous().then(value => {
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
}); // errors would now be propagated up
});
Or do you just take it out the Promise constructor altogether as suggested here?
async function outerFunction() {
const value = await somethingAsynchronous();
return new Promise((resolve, reject) => {
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
});
}
But what if you have several await statements in the outerFunction(), i.e. a linear code block calling several asynchronous functions. Would you then have to create and return a new Promise every time?
But then how do you account for code such as this?
async function outerFunction() {
if (someSynchronousCheck()) {
return 'Nope. Try again.' // another reject case
}
const value = await somethingAsynchronous();
// ...
}
I have the feeling that I'm making this more complicated than it should be. I'm trying to avoid nesting callbacks/chaining then/catch blocks without creating more problems in the future.
My final question is, why is the callback passed to a Promise not inherently async? It is already wrapped within a promise and expects the resolve/reject functions to be called asynchronously.
You do this:
async function outerFunction() {
const value = await somethingAsynchronous();
if (value === something) {
return 'It Worked!';
}
throw Error('Nope. Try again.');
}
Using async wraps the result of outerFunction with a Promise.
If you want that wrapping promise to resolve to something, just return it from the async function. If you want the wrapping promise to be rejected, throw an error inside the async function.
But then how do you account for code such as this?
async function outerFunction() {
if (someSynchronousCheck()) {
throw Error('Nope. Try again.');
}
const value = await somethingAsynchronous();
// ...
}
new Promise(async (resolve, reject) => { ... }) is relatively new antipattern. It results in creating 2 promise objects instead of 1, uncaught errors that happen inside constructor cannot be caught with try..catch and result in unhandled rejection.
Considering that promise asynchronous code can be handled with async..await, current use case for Promise constructor is non-promise asynchronous code, e.g.:
new Promise(resolve => setTimeout(resolve, 1000))
When Promise constructor contains synchronous code or involves other promises, a promise should be constructed with async function. A drop-in replacement is async IIFE:
return (async (resolve, reject) => {
const value = await somethingAsynchronous();
if (value === something) {
return 'It worked!';
} else {
throw 'Nope. Try again.';
}
})();
If the need for Promise constructor still presents when being used together with async, Promise constructor should be moved down in hierarchy so it won't wrap any async function.
My final question is, why is the callback passed to a Promise not inherently async? It is already wrapped within a promise and expects the resolve/reject functions to be called asynchronously.
async function isn't just a function that is executed asynchronously, it returns another promise that is supposed to be utilized - or at least handled with catch. Promise isn't supposed to utilize a promise that is returned from constructing function.
The constructor can resolve on same tick and doesn't necessarily have to be asynchronous.
Promise.resolve(1);
is similar to
Promise(resolve => resolve(1))
and not to
Promise(resolve => setTimeout(() => resolve(1)))
You can also chain the promises yourself by simply doing this:
return new Promise((resolve, reject) => {
somethingAsynchronous().then((value) => {
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
}, (error) => { reject(error); });
});
I've been using this for some time and it works perfectly for me.
I have some javasript code that takes an existing promise
(say, the promise returned by fetch()) and adds value
(say, then/catch listeners for debugging, or maybe more):
let myFetch = function(url) {
return fetch(url).then(function(value) {
console.log("fetch succeeded: value=",value);
return value;
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
throw reason;
});
};
I found myself modifying the above code so that the listeners are added only if some condition is true:
let myFetch = function(url) {
let promise = fetch(url);
if (some condition) {
promise = promise.then(function(value) {
console.log("fetch succeeded: value=",value);
return value;
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
throw reason;
});
}
return promise;
};
Now I'm wondering, does it really make sense for myFetch to return the new promise returned by "then"
(actually catch which is shorthand for another "then") as above,
or would it make more sense for it to return the original promise (with the added listeners)?
In other words, I'm thinking of leaving out the second "promise =",
so that the code will look like this instead:
let myFetch = function(url) {
let promise = fetch(url);
if (some condition) {
promise.then(function(value) {
console.log("fetch succeeded: value=",value);
return value;
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
throw reason;
});
}
return promise;
};
Is that effectively different from the previous version?
Is either version preferable, and if so, why?
If your only use case is logging something in then/catch – it shouldn't matter as long as everything goes well. Things get more messed if you get an exception. Consider these two examples:
Return original promise
function myFetch() {
let promise = new Promise(function (resolve, reject) {
resolve(100);
});
promise.then(function () { throw new Error('x'); });
return promise;
}
myFetch().then(function () {
console.log('success!');
}).catch(function (e) {
console.error('error!', e)
});
The result is success and the error thrown in the inner then might get swallowed in some promise libraries (although the most popular ones such as Bluebird handle this and you get additional error Unhandled rejection Error: x).
The error might also get swallowed when using native Promises in some environments.
Returning modified promise
function myFetch() {
let promise = new Promise(function (resolve, reject) {
resolve(100);
});
promise = promise.then(function () { throw new Error('x'); });
return promise;
}
myFetch().then(function () {
console.log('success!');
}).catch(function (e) {
console.error('error!', e)
});
Now the result is error! Error: x.
Well, if your success handler returns the value and your rejection handler throws the error - then it is basically the identity transformation for the promise.
Not only do you not need to do that promise = promise.then you don't even need to return the values:
let myFetch = function(url) {
let promise = fetch(url);
if (some condition) {
promise.then(function(value) {
console.log("fetch succeeded: value=",value);
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
});
}
return promise;
};
That said, if you're using ES6 and let, you can use arrow functions which makes this a little nicer anyway:
let myFetch = function(url) {
let promise = fetch(url);
if (some condition) {
promise.then(value => console.log("fetch succeeded: value=",value))
.catch(reason => console.log("fetch failed: reason=",reason));
}
return promise;
};
Some promise libraries like bluebird provide a tap utility for this. The only problem is that if ever fetch adds support for promise cancellation, you are breaking the chain with the if (some condition) handler if you're not chaining it.
You're promise branching. In the second case, you're effectively branching the promise chain into two promise chains, because once a caller calls myFetch:
myFetch("localhost").then(request => { /* use request */ } );
then promise will have had .then called on it twice (once inside myFetch to do the console logging, and again here).
This is fine. You can call .then on the same promise as many times as you like, and the functions will execute together in the same order whenever promise resolves.
But, importantly, each function represents a branch off of the original promise, independent of every other branch. This is why you don't need to return or rethrow anything after your console.log: Nobody's listening on that branch, specifically, the caller of myFetch is not affected.
This is a good fit for logging IMHO, but there are subtle timing and error handling differences to be aware of when doing anything more:
var log = msg => div.innerHTML += msg + "<br>";
var myFetch = url => {
var p = Promise.resolve({});
p.then(() => log("a")).then(() => log("b"));
return p;
}
myFetch().then(() => log("1")).then(() => log("2")).catch(log); // a,1,b,2
<div id="div"></div>
This emits a,1,b,2. As you can see, there are two chains going on here, advancing in parallel. It makes sense when you think about when promise is resolved, but it can be surprising.
The other subtlety is that error handling is also per branch (one branch will never fail another). In fact, the above code has a bug. Did you spot it? There should be a catch after .then(() => log("b")), or errors about anything you do in that branch end up unhandled or swallowed in some environments.
Say I have the following Promise chain:
var parentPromise = Promise.resolve()
.then(function () {
var condition = false;
if (condition) {
return parentPromise.cancel('valid reason');
} else {
return Promise.resolve()
.then(function () {
var someOtherCondition = true;
if (someOtherCondition) {
console.log('inner cancellation');
return parentPromise.cancel('invalid reason');
}
});
}
})
.catch(Promise.CancellationError, function (err) {
console.log('throwing');
if (err.message !== 'valid reason') {
throw err;
}
})
.cancellable();
The above never enters the catch.
If we swap condition to true, the inner cancellation is never hit, but the catch is still not triggered.
removing the .cancellable at the end , and replacing all instances of parentPromise.cancel() with explicit throw new Promise.CancellationError() "fixes" the problem. What I don't understand is why?
Why was the original approach not working?
I am using bluebird 2.3.11.
cancellable() creates cancellable promises and only they throw CancellationError by default, when cancel function is called with out any reason.
In your case, you are making the promise cancellable only after attaching the catch handler. But the promise is not cancellable yet. So, cancel function call will not raise Promise.CancellationError.
You need to change the structure of the code, like this
then(function(..) {
...
})
.cancellable()
.catch(Promise.CancellationError, function (err) {
...
});
Note: I would recommend promising with its beautiful Promise constructor function. It is similar to the ECMA Script 6 specification.
new Promise(function(resolve, reject) {
...
});