As I understand it, resolve and reject are specified in the callback provided to the promise constructor and invoked within the callback. But resolve and reject (I think) always do the same thing, so why do they even have to be specified?
EDIT: I should clarify: I do not think that resolve does the same thing as reject; I mean resolve seems to always resolve and reject always reject -- is there a way to pass in a function instead of resolve that does something else?
EDIT: To further clarify as I did in a comment below: if a Promise constructor takes a function which in turn takes resolve and reject and these are always to be called in the function, why must resolve and reject be specified as arguments at all?
Final Edit: I tried with foo instead of resolve and it still worked. How can that be? I did not define foo at all?? I probably am not understanding something about javascript beyond promises. code snippet where i changed resolve to foo
When you create a JS Promise, you expect some task to happen asynchronously. It is possible, that this task can succeed or fail. The success and failure are defined based on what the task is.
When you create a new Promise, you as the creator must specify what to do when the task is successful or is a failure. To communicate the same to the consumer of the promise resolve/reject come handy.
So when the consumer uses a then to check the output of the promise. what ever is returned by resolve is given to the consumer.
When the consumer uses a catch to check the output of the promise. whatever is returned by the reject is given to the consumer.
import {Promise} from 'es6-promise'
const foo = true;
const bar = new Promise((resolve, reject) => {
if (foo) {
resolve(4);
} else {
reject(2);
}
})
bar.then((value) => console.log(value)).catch((value) => console.log(value));
you can run the above sample by changing the value of foo to true/false and see for yourself what the output is.
I assume you are referring to the then() function which takes two callbacks, one when the promise is resolved and one when it is resolved. The pattern often looks like this:
myPromise.then(result => {
// do something here
},
error => {
}
);
If the promise is resolved everything went as expected and the first callback should proceed as normal. If the promise is rejected the second callback should perform error handling, possibly displaying an appropriate message to the user. These never do the same thing.
If you have logic that should always occur, whether there is an error or not, you should chain another call to then():
myPromise.then(result => {
// do something here
},
error => {
// do something here
}
).then(result => {
// do something here
}
);
This is conceptually the same as try...catch...finally from many programming languages.
Resolve and reject do not "do the same thing".
When you resolve a promise you are saying "this is the successful response of this promise".
When you reject a promise you are saying "this promise failed because...".
For example:
Imagine in the following examples accountApiRequest() actually does an API request to fetch an account. If there is no account it will most likely return null or throw an exception.
When a promise is successful you will resolve it. When a promise is not successful it will be rejected.
Successful promise
function accountApiRequest() {
return {
id: 1234,
username: 'Jim',
}
}
function getAccount() {
return new Promise(function(resolve, reject) {
account = accountApiRequest()
if (!account) {
return reject('Unable to fetch account')
}
return resolve(account)
});
}
getAccount().then(console.log).catch(console.log)
Failed promise
function accountApiRequest() {
return null
}
function getAccount() {
return new Promise(function(resolve, reject) {
account = accountApiRequest()
if (!account) {
return reject('Unable to fetch account')
}
return resolve(account)
});
}
getAccount().then(console.log).catch(console.log)
In the Promise constructor we have a callback which takes the resolve and reject methods as its arguments. After a Promise is created using the constructor a Promise can have three states:
Pending
Resolved
Rejected
When a promise is resolved then the callback which is passed in the then() method is called. If the promise is rejected the callback which is passed in the catch() method is called. For example:
const prom = new Promise((resolve, reject) => {
setTimeout(() => {
if(Math.random() > 0.5) {
resolve('resolved');
} else {
reject('rejected');
}
}
, 2000)
});
prom.then((val) => console.log('succes: ' + val))
.catch((val) => console.log('fail: ' + val))
In our example the resolve or reject is called in a setTimeout, therefore the status of the promise is the first 2 seconds pending. After the setTimeout is expired the Promise is either resolved or rejected based on the value of Math.random(). Then either the then or the catch callback is executed to handle the result of the promise.
When the promise is rejected the callback passed in the catch method is called
When the promise is resolved the callback passed in the then method is called
I had the same question when I read about Promises. And I came to understanding that you don't need to define resolve and reject functions by yourself (they will be ignored and standard resolve/reject functions will be called anyway): I think of them as special functions which let you indicate whether to resolve or reject the promise by calling one of them; and what value to return in each case by passing arguments to these functions. It doesn't matter how you name them in promise constructor - first argument is resolve function and the second - reject.
I have spent a ton of time trying to figure this out. Unfortunately, for anyone well versed with JS syntax, won't really understand the question...
For me, it was clear, after unraveling the concise JS syntactic sugar:
function promise_executor(resolve_callback_provided_by_promise_instance, reject_callback_provided_by_promise_instance) {
if (<some_condition>) {
// success
var response='bla';
resolve_callback_provided_by_promise_instance(response);
} else {
// fail
var error=new Error('failed');
reject_callback_provided_by_promise_instance(error);
}
}
var promise_instance = new Promise(promise_executor);
So the reject and resolve are just random names for callbacks that the promise instance will call.
They have proper interfaces, like both will take 1 param of type object. That param will be passed to either the Promise.then() or the Promise.catch().
resolve
reject
Then, when you use them, it is like this:
function success_callback(param_from_resolve) {
console.log('success() ', param_from_resolve);
}
function fail_callback(param_from_reject) {
console.log('fail(): ', param_from_reject);
}
promise_instance
.then(success_callback) // this will be the var 'resolve' from above
.catch(fail_callback); // this will be the var 'error' from above
The whole code flow is like this:
<at some point, your promise_executor() code is called with 2 functions, as parameters>
// obviously, the promise_executor() function has its own name in the Promise object
promise_executor(Promise.reject, Promise.resolve);
<promise_executor() will -hopefully- call either the resolve or the reject callback, which will in turn trigger either the callback of then() or catch()>
<say we have then()>
<the Promise code will take the response param from resolve_callback_provided_by_promise_instance(response) and give it to the callback from then()>
param_from_resolve(response)
It will go down something like this (THIS IS NOT what actually happens, as in reality, the micro-queue, the eventloop, etc. gets involved - this is a linearized version of the gist of what is happening):
What you see:
var promise = new Promise(promise_executor);
What is executed:
function Promise.constructor(exec_callback_fnc) {
var this = object();
this.exec_callback_fnc = exec_callback_fnc; // this is "promise_executor"
return this;
}
What you see:
// at some point in "this.exec_callback_fnc":
resolve_callback_provided_by_promise_instance(response)
What is executed:
function promise_instance.resolve(param) {
this.param_from_resolve = param;
promise_instance.resolved = true;
}
What you see:
promise_instance
.then(success_callback)
What is executed:
promise_instance.exec_callback_fnc(promise_instance.resolve, promise_instance.reject);
if (promise_instance.resolved) {
function promise_instance.then(callback_fnc) {
callback_fnc(this.param_from_resolve); // this is "success_callback"
}
}
Finally, let's see, how the syntax makes this whole thing more concise:
function promise_executor(resolve_callback_provided_by_promise_instance,
reject_callback_provided_by_promise_instance) {
if (<some_condition>) {
// success
var response='bla';
resolve_callback_provided_by_promise_instance(response);
} else {
// fail
var error=new Error('failed');
reject_callback_provided_by_promise_instance(error);
}
}
var promise_instance = new Promise(promise_executor);
function success_callback(param_from_resolve) {
console.log('success() ', param_from_resolve);
}
function fail_callback(param_from_reject) {
console.log('fail(): ', param_from_reject);
}
promise_instance
.then(success_callback)
.catch(fail_callback);
Will become:
new Promise((resolve_callback_provided_by_promise_instance,
reject_callback_provided_by_promise_instance) => {
if (<some_condition>) {
// success
var response='bla';
resolve_callback_provided_by_promise_instance(response);
} else {
// fail
var error=new Error('failed');
reject_callback_provided_by_promise_instance(error);
}})
// "response" -> "param_from_resolve"
.then((param_from_resolve) => { console.log('success() ', param_from_resolve); })
// "error" -> "param_from_reject"
.catch((param_from_reject) => { console.log('fail(): ', param_from_reject); });
Related
I am new to typescript / javascript, so I don't know much about promises. Here is my use-case: I am creating three different promises inside my cloud-function and then returning it with Promise.all([promise1, promise2, promise3]). Each of these promises are created inside a function with "return Promise...".
My question is, when I add ".catch" inside these functions, will Promise.all still work?. Does it make any difference returning someServiceThatCreatesPromise() with and without .catch()?
Code
export async function myCloudFirestoreFunction() {
try {
const myFirstPromise = createFirstPromise()
const mySecondPromise = createSecondPromise()
const thirdPromise = createThirdPromise()
return Promise.all([
myFirstPromise,
mySecondPromise,
myThirdPromise
]);
} catch (err) {
functions.logger.err(`Something bad happened, See: ${(err as Error).message}`
}
}
// Difference with and without `.catch`?
function createFirstPromise() {
return someServiceThatCreatesPromise().catch((err) => { // LOGGING });
}
// Difference with and without `.catch`?
function createSecondPromise() {
return someServiceThatCreatesPromise().catch((err) => { // LOGGING });
}
// Difference with and without `.catch`?
function createThirdPromise() {
return someServiceThatCreatesPromise().catch((err) => { // LOGGING });
}
Adding .catch inside createNPromise won't affect anything assuming all your Promises resolve and do not reject.
However, if one of the Promises rejects and you catch it within the .catch method, then it won't work as you're expecting unless you re-throw that error and catch it again inside the try/catch in your myCloudFirestoreFunction function.
async function myCloudFirestoreFunction() {
try {
const result = await Promise.all([
createFirstPromise(),
createSecondPromise()
]);
} catch (error) {
console.log({ error });
}
}
function createFirstPromise() {
return Promise.reject("Oof").catch((e) => {
// do work, e.g. log, then
// pass the error forward so that it can be caught
// inside the caller
throw e;
});
}
function createSecondPromise() {
return Promise.resolve("value");
}
myCloudFirestoreFunction();
Alternatively, you just catch errors inside the caller (myCloudFirestoreFunction) instead of catching them separately.
async function myCloudFirestoreFunction() {
const result = await Promise.all([
createFirstPromise(),
createSecondPromise()
]).catch((err) => console.log({ err }));
}
function createFirstPromise() {
return Promise.reject("Oof");
}
function createSecondPromise() {
return Promise.resolve("value");
}
myCloudFirestoreFunction();
when I add ".catch" inside these functions, will Promise.all still work?
Calling catch() on a promise does not in any way change the way the original promise works. It is just attaching a callback that gets invoked when the first promise becomes rejected, and also returning another promise that resolves after the original promise is fulfilled or rejected.
Does it make any difference returning someServiceThatCreatesPromise() with and without .catch()?
It would not make any difference to the code that depends on the returned promise. Both the original promise and the one returned by catch() will tell downstream code when the original work is done by becoming fulfilled.
I suggest reading comprehensive documentation on promises, for example:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
There is a diagram in there that you can follow to understand what happens at each turn. Also in that document, you will read:
Processing continues to the next link of the chain even when a .then() lacks a callback function that returns a Promise object. Therefore, a chain can safely omit every rejection callback function until the final .catch().
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.
This is tightly coupled to Chaining .then() calls in ES6 promises ...
I tried this with some functions that make up a chain of promises, so basically:
var PromiseGeneratingMethod = function(){
return p = new Promise((resolve, reject) =>{
resolve(1)
});
}
var inBetweenMethod = function(){
return PromiseGeneratingMethod()
.then((resolved) => {
if(resolved){
console.log('resolved in between');
//return resolved
/* this changes output to
resolved in between
resolved at last*/
}else{
console.log('something went terribly wrong in betweeen', resolved);
}
});
}
inBetweenMethod().then((resolved) =>{
if(resolved){
console.log('resolved at last')
}else{
console.log('something went terribly wrong', resolved);
}
})
/* ouput:
resolved in between
something went terribly wrong undefined*/
I don't understand why it is like that. doesn't have a Promise just ONE associated return value? why can I change that value in every then? It seems irrational to me. A Promise Object can only have one return value and I thought every then handler will receive the same parameter after the Promise gets resolved?
This way, having two Methods which call then() on the same Promise, the latter one (in asynchronous environments you never know what that is...) will ALWAYS get an empty result, except if EVERY then returns the desired value
If I got it right, the only good thing is that you can build a then().then().then() chain to make it almost synchronous (by returning arbitrary values in every then()) but you still could achieve the same with nested Promises, right?
Can someone help me understand why es6 Promises work that way and if there are more caveats to using those?
doesn't have a promise just ONE associated return value?
Yes.
why can I change that value in every then?
Because every .then() call does return a new promise.
having two methods which call then() on the same Promise
That's not what you're doing. Your then callbacks are installed on different promises, that's why they get different values.
You could do
function inBetweenMethod() {
var promise = PromiseGeneratingMethod();
promise.then(resolved => { … }); // return value is ignored
return promise;
}
but you should really avoid that. You already noticed that you can get the expected behaviour with
function inBetweenMethod() {
var promise = PromiseGeneratingMethod();
var newPromise = promise.then(value => {
…
return value;
});
return newPromise;
}
where the newPromise is resolved with the value that is returned by the callback - possibly the same value that promise fulfilled with.
you are using .then() handler twice, do the following:
var PromiseGeneratingMethod = function(){
return new Promise((resolve, reject) =>{
if (myCondition) resolve(1)
if (!myCondition) reject("failed")
});
}
var inBetweenMethod = function(){
return PromiseGeneratingMethod()
}
inBetweenMethod().then((resolved) =>{
console.log(resolved)
}).catch(function(err) {
console.log(err)
})
I am not able to catch the Exception/Error that is happening inside of the Resolve promise.
Can someone explain if there is a way to catch this error:
createAsync = function() {
var resolve, reject,
promise = new Promise(function(res, rej) {
resolve = res;
reject = rej;
});
promise.resolve = resolve;
promise.reject = reject;
return promise;
};
promise = createAsync()
promise.then(function myresolver(){
throw Error('wow')
})
// Catching the error here
try {
promise.resolve()
} catch(err) {
console.log( 'GOTCHA!!', err );
}
EDIT:
Let me explain me better, I'm trying to create an API and the user only have access to the promise resolve/reject part:
// This is my API user does not have access to this part
promise = createAsync()
promise
.then(function(fun) {
if (typeof fun != 'function')
throw Error('You must resolve a function as argument')
}).catch(function(err){
// Some internal api tasks...
return Promise.reject(err)
})
Now the solution I would like to give him, but does not work:
// Here is where the user can resolve the promise.
// But must be able to catch the error
promise.catch(function(err){
console.log("GOTCHA", err)
})
promise.resolve('This must be a function but is a string');
Any ideas?
More info:
Yes is an antipattern for the common usage. This is a remote procedure call API, so the user must be able to reject or resolve. And the calls are async so the best approach is to use promises. I wouldn't say is an antipattern for this case. Actually is the only pattern. (I can't use Async/Await)
Let me explain me better, I'm trying to create an API and the user only have access to the promise resolve/reject part.
Now the solution I would like to give him does not work.
Any ideas?
Don't put everything onto that initial promise object. It sounds what you really need to expose to your user is a) a way to resolve (which includes rejection) and b) a way to get the result (promise) back. So all you need to do is give him a function:
function createAsync() {
var resolve;
var promise = new Promise(function(r) { resolve = r; })
.then(function(fun) {
if (typeof fun != 'function')
throw Error('You must resolve a function as argument')
}).catch(function(err){
// Some internal api tasks...
throw err;
});
return function(value) {
resolve(value);
return promise;
};
}
and he'd use it like
var start = createAsync();
// possibly later
start('This must be a function but is a string').catch(function(err){
console.log("GOTCHA", err)
});
// or
start(Promise.reject(new Error('doomed to fail'))).catch(…);
but actually this pattern is still convoluted and much too complicated. Simply give your createAsync function a parameter (that might receive a promise if the user needs to delay passing the fun) and be done with it:
function createAsync(valu) {
return Promise.resolve(val).then(function(fun) {
if (typeof fun != 'function')
throw Error('You must resolve a function as argument')
}).catch(function(err){
// Some internal api tasks...
throw err;
});
}
createAsync('This must be a function but is a string').catch(function(err) {
console.log("GOTCHA", err)
});
// or
createAsync(new Promise(function(resolve) {
// later
resolve('This must be a function but is a string');
})).catch(function(err) {
console.log("GOTCHA", err)
});
Warning: as pointed out by #torazaburo "Exporting" the resolve and reject functions provided to the executor (the function passed to the promise constructor), in your case by polluting the promise object with additional properties, is an anti-pattern. The entire point of the design of the promise constructor is that resolving and rejecting is isolated within the executor. Your approach allows any random person who happens to acquire one of your promise objects to resolve or reject it, which is almost certainly not good program design.
With that warning in mind, your call to promise.resolve() doesn't reject the promise ... your promise.then ... throw will "reject" THAT promise (the one returned by .then) not the one in promise
rewrite your code as follows:
promise = createAsync()
promise // the call to promise.resolve will resolve THIS promise, so .then will execute
.then(function myresolver(){
throw Error('wow')
}) // .then returns a NEW promise, in this case rejected with Error('wow')
.catch(function(e) {
console.log("GOTCHA", e)
});
promise.resolve()
and you'll see the expected GOTCHA in the console
Jaromanda's answer already explained that you must use a rejection handler or catch to handle asynchronous errors using promises.
Also make sure to read torazaburo's comment explaining why your approach is an anti-pattern.
If you really must use this approach and also require try/catch for some reason, ES7 gives you a way via async functions
(async function f() {
const createAsync = function() {
var resolve, reject,
promise = new Promise(function(res, rej) {
resolve = res;
reject = rej;
});
promise.resolve = resolve;
promise.reject = reject;
return promise;
};
const promise = createAsync();
promise.resolve();
// Catching the error here
try {
await promise.then(function() {
throw Error('wow')
});
} catch (err) {
console.log('GOTCHA!!', err);
}
})()
Does anybody have any thoughts about ES6 promises, I'm using them in my Node app and I love them, on the most part. But Ive found that if I get some sort of error in a resolve callback, it won't throw an error or execute the reject callback, and its leaving me with my server hanging infinitely.
For now I've resorted to doing this, and manually rejecting the promise with the caught error, but I'm not sure if this a great way to handle, and/or if I should be using promises at all.
this.dataStore.set(newID, value).then( (foo) => {
try{
this.var = foo;
res({val: foo});
}catch(e){
rej(e);
}
}, (e) => {
rej(e);
});
I think the confusion is arising from the fact that, based on your use of res, and rej here, you are likely calling this from within a promise constructor, along the lines of
function setStore(newID, value) {
return new Promise(function(res, rej) {
this.dataStore.set(newID, value).then( (foo) => {
try{
this.var = foo;
res({val: foo});
}catch(e){
rej(e);
}
}, (e) => {
rej(e);
});
});
}
By the way, the (e) => { rej(e); } part at the end could be rewritten as e => rej(e), which in turn could be rewritten as rej.
But anyway, you don't need any of that surrounding machinery to create and return your own promise, because this.dataStore.set and/or the ensuing call to then already creates a promise, which you can return as is. Instead of creating your own new promise, and then resolving your new promise with the little hash based on the result passed to then, just return the hash--that will become the value of the resulting promise. Instead of rejecting your new promise when the call to dataStore.set fails, just let the failed promise be itself.
So you shouldn't need to do anything more complicated than
function setStore(newID, value) {
return this.dataStore.set(newID, value).then(foo => {
this.var = foo;
return {val: foo};
});
}
An error occurring in the this.var = foo; return {val: foo}; part (but how could it?) will automatically throw the promise into failure state. A failure resulting from this.dataStore.set will yield a failed promise as is, and there is no need to catch it and rethrow it--the failure will progress merrily down the chain.
Use this as:
setStore('abc', 123)
.then(hash => console.log(hash.val))
.catch(e => console.log("The sky is falling", e));
As a matter of clarification, in the following:
promise.then(success, failure)
an error arising in the success callback is not handled in the failure callback. A failure in the success callback would be handled in successive stages of the chain. You could handle a failure in success (or promise itself) with
promise.then(success).catch(failure)