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.
Related
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));
}
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);
}
})()
I just implemented my first function that returns a promise based on another promise in AngularJS, and it worked. But before I decided to just do it, I spent 2 hours reading and trying to understand the concepts behind promises. I thought if I could write a simple piece of code that simulated how promises worked, I would then be able to conceptually understand it instead of being able to use it without really knowing how it works. I couldn't write that code.
So, could someone please illustrate in vanilla JavaScript how promises work?
A promise is basically an object with two methods. One method is for defining what to do, and one is for telling when to do it. It has to be possible to call the two methods in any order, so the object needs to keep track of which one has been called:
var promise = {
isDone: false,
doneHandler: null,
done: function(f) {
if (this.isDone) {
f();
} else {
this.doneHandler = f;
}
},
callDone: function() {
if (this.doneHandler != null) {
this.doneHandler();
} else {
this.isDone = true;
}
}
};
You can define the action first, then trigger it:
promise.done(function(){ alert('done'); });
promise.callDone();
You can trigger the action first, then define it:
promise.callDone();
promise.done(function(){ alert('done'); });
Demo: http://jsfiddle.net/EvN9P/
When you use a promise in an asynchronous function, the function creates the empty promise, keeps a reference to it, and also returns the reference. The code that handles the asynchronous response will trigger the action in the promise, and the code calling the asynchronous function will define the action.
As either of those can happen in any order, the code calling the asynchronous function can hang on to the promise and define the action any time it wants.
For the simplicity to understand about the promises in Javascript.
You can refer below example. Just copy paste in a new php/html file and run.
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
function test(n){
alert('input:'+n);
var promise = new Promise(function(fulfill, reject) {
/*put your condition here */
if(n) {
fulfill("Inside If! match found");
}
else {
reject(Error("It broke"));
}
});
promise.then(function(result) {
alert(result); // "Inside If! match found"
}, function(err) {
alert(err); // Error: "It broke"
});
}
</script>
</head>
<body>
<input type="button" onclick="test(1);" value="Test"/>
</body>
</html>
Click on Test button,
It will create new promise,
if condition will be true it fulfill the response,
after that promise.then called and based on the fulfill it will print the result.
In case of reject promise.then returns the error message.
Probably the simplest example of promises usage looks like that:
var method1 = (addings = '') => {
return new Promise(resolve => {
console.log('method1' + addings)
resolve(addings + '_adding1');
});
}
var method2 = (addings = '') => {
return new Promise(resolve => {
console.log('method2' + addings)
resolve(addings + '_adding2');
});
}
method1().then(method2).then(method1).then(method2);
// result:
// method1
// method2_adding1
// method1_adding1_adding2
// method2_adding1_adding2_adding1
That's basic of basics. Having it, you can experiment with rejects:
var method1 = (addings = '*') => {
return new Promise((resolve, reject) => {
console.log('method1' + addings)
resolve(addings + '_adding1');
});
}
var method2 = (addings = '*') => {
return new Promise((resolve, reject) => {
console.log('method2' + addings)
reject();
});
}
var errorMethod = () => {
console.log('errorMethod')
}
method1()
.then(method2, errorMethod)
.then(method1, errorMethod)
.then(method2, errorMethod)
.then(method1, errorMethod)
.then(method2, errorMethod);
// result:
// method1*
// method2*_adding1
// errorMethod
// method2*
// errorMethod
// method2*
As we can see, in case of failure error function is fired (which is always the second argument of then) and then next function in chain is fired with no given argument.
For advanced knowledge I invite you here.
please check this simple promise code. this will help you to better understand of promise functionality.
A promise is an object that may produce a single value some time in the future: either a resolved value, or a reason that it’s not resolved. A promise may be in one of 3 possible states: fulfilled, rejected, or pending. Promise users can attach callbacks to handle the fulfilled value or the reason for rejection.
let myPromise = new Promise((resolve, reject)=>{
if(2==2){
resolve("resolved")
}else{
reject("rejected")
}
});
myPromise.then((message)=>{
document.write(`the promise is ${message}`)
}).catch((message)=>{
document.write(`the promise is ${message}`)
})
check this out
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) {
...
});