There's a subtlety in using Promises with event handlers or any code that's executed asynchronously.
Promises are supposed to be used for future values, with the reject
handler acting like an exception handler, so that the Promise constructor actually catches real exceptions and turns them into reject
s (from the polyfill; I assume the browser-implemented code works similarly):
try {
resolver(resolvePromise, rejectPromise);
} catch(e) {
rejectPromise(e);
}
But asynchronous code runs outside the execution unit that runs resolver()
, so any exceptions there have to be caught explicitly:
<button id=one>Click Me</button>
<button id=two>Don't Click Me</button>
<script>
var promise = new Promise(function(resolve, reject){
document.querySelector('#one').onclick = function(){
resolve ('You Clicked!');
};
document.querySelector('#two').onclick = function(){
throw new Error ('You Clicked!');
};
});
promise.then(function(msg){
alert('Button clicked: '+msg);
}, function(err){
alert('Error caught:'+err.message);
});
</script>
Clicking button two gives an uncaught exception error, not the alert from the then()
. You have to catch the exceptions:
document.querySelector('#two').onclick = function(){
try {
throw new Error ('You Clicked!');
} catch (e) {
reject(e);
}
};
or resolve to a new promise that can catch the error:
document.querySelector('#two').onclick = function(){
resolve(new Promise(function (){
throw new Error ('You Clicked!');
}));
};
The other subtlety (which is obvious from the definition of a Promise) is that Promises are once-only; in the above code, clicking either button settles the Promise and further clicking does nothing. Event handlers can be called multiple times, so you have to think about what you intend.
Leave a Reply