I code JavaScript quite a bit and although I think I do understand workings of promises I'm not sure if I fully understand advantages that promises bring to JS world. Consider code below, simply asynchronous calls with callbacks containing furhter calls and so on.
(function doWorkOldSchool() {
setTimeout(function() {
// once done resolve promise
console.log("work done");
setTimeout(function goHome() {
// once done resolve promise
console.log("got home");
try {
setTimeout(function cookDinner() {
// this exception will not be caught
throw "No ingredients for dinner!";
console.log("dinner cooked");
setTimeout(function goToSleep() {
// once done resolve promise
console.log("go to sleep");
}, 2000);
}, 2000);
} catch (ex) {
console.log(ex);
}
}, 2000);
}, 2000);
}());
One problem I see with this:
Exceptions thrown inside of callbacks are useless. Is it correct to say that as throw calls happen these throw calls are out of scope hence the exception cannot be called and bubbles all the way to the top? How this sort of exception can be dealt with?
Second problem I see that this nesting business might get really deep and even though you can keep callback functions code outside of the setTimeout code, it might become a mess.
So first could someone please clarify if there is anything else that is obvious problem or advantage of this sort of coding?
Now, below I prepared program that does the same thing really, but this time using promises:
function doWork() {
return new Promise(function(res, rej) {
// do your asynchronous stuff
setTimeout(function() {
// once done resolve promise
res("work done");
}, 2000);
});
}
function goHome(succ) {
console.log(succ);
return new Promise(function(res, rej) {
// do your asynchronous stuff
setTimeout(function() {
// once done resolve promise
res("got home");
}, 2000);
});
}
function cookDinner(succ) {
console.log(succ);
//if exception thrown here it will be caught by chained err handler
throw "No ingredients for dinner Exception!";
return new Promise(function(res, rej) {
// do your asynchronous stuff
setTimeout(function() {
// something went wrong so instead of using throw we reject the promise
rej("No ingredients for dinner!");
// once done resolve promise
}, 2000);
});
}
function goToSleep(succ) {
console.log(succ);
return new Promise(function(res, rej) {
// do your asynchronous stuff
setTimeout(function() {
// once done resolve promise
res("zzz... zz..");
}, 2000);
});
}
doWork()
.then(goHome)
.then(cookDinner)
.then(goToSleep)
.then(function(succ) {
console.log(succ);
}, function(err) {
console.log(err);
});
Comparing to previous solution I see no obvious problems with this approach, apart from you obviously must have understanding of promises to code/maintain this thing. Advantages would be however:
the exception thrown INSIDE OF handlers will be caught in err handler that is chained somewhere further.
the rejected Promises will be caught by chained err handler
the code is much cleaner
Now, is my understanding correct or are there any other advantages/disadvantages of each approach?
Your code is employing some anti patterns, you should never create promises (e.g. by new Promise, "deferreds" and so on) in application code. You must also never mix callbacks with promises because then you lose exception bubbling (the point of promises) and make your code super verbose.
You can use a library or implement a delay yourself:
function delay(ms, val) {
return new Promise(function(res){setTimeout(res.bind(null, val), ms);});
}
Then:
function doWork() {
return delay(2000, "work done");
}
doWork()
.then(function(succ) {
console.log(succ);
return delay(2000, "got home");
})
.then(function(succ) {
console.log(succ);
// Never throw strings, yet another anti-pattern
throw new Error("No ingredients for dinner Exception!");
// Pointless to add code after throw
})
.then(function(succ) {
return delay(2000, "zzz.. zz..");
})
// Don't use the second argument of .then, use .catch
.catch(function(err) {
console.log("error: ", err);
});
Why should you use .catch instead of the second argument? Well, compare to how you would write it synchronously:
try {
doWork();
console.log("work done");
sleep(2000);
console.log("got home");
sleep(2000);
throw new Error("No ingredients for dinner Exception!";)
sleep(2000);
console.log("zzz.. zz..");
}
catch(e) {
console.log("error: ", err);
}
Get it?
Promises can only return/decide on a value once, so once a value is chosen it cannot be changed. So if a user clicks on a div once the Promise only executes once
Ex:
p = new Promise(function (res, rej) {
var b = document.getElementById('helloWorld');
b.onclick = function() {
res(prompt('value'));
};
});
p.then(function(val) { console.log(val); });
There will always be only one value that will be in the log.
This would be useful for GUI and controls for games/applications.
Also you could have two event listeners inside of the Promise, this is useful for loading images and files. Promises react even after you have added a succeed or failure handler, so you create a function for a success though the failure function is created later which normal event handlers would generate errors if it failed, though Promises don't and call the function later when it is created. This allows you to focus more on how to react to an event and not worry about the timing of things. Very useful for loading things.
Amazing Page On Promises
Related
It's easy to forget to use try/catch in an async function or otherwise fail to catch all possible errors when working with promises. This can cause an endless "await" is the Promise is never resolved nor rejected.
Is there any way (such as via a proxy or altering the promise constructor) to cause an async function or other promises to be rejected if there is an uncaught error? The following shows a generalized case. I'm looking for some way to get past the "await" (as in "p" should be rejected when the error is thrown) without fixing "badPromise".
async function badPromise() {
const p = new Promise((res) => {
delayTimer = setTimeout(() => {
console.log('running timeout code...');
if (1 > 0) throw new Error('This is NOT caught!'); // prevents the promise from ever resolving, but may log an error message to the console
res();
}, 1000);
});
return p;
}
(async () => {
try {
console.log('start async');
await badPromise();
console.log('Made it to the end'); // never get here
} catch (e) {
console.error('Caught the problem...', e); // never get here
}
})();```
Promises already reject in the case of an uncaught synchronous error:
in a Promise constructor, for synchronous (thrown) errors
If an error is thrown in the executor, the promise is rejected.
in onFulfilled and onRejected functions, such as in then and catch
If a handler function: [...] throws an error, the promise returned by then gets rejected with the thrown error as its value.
in async functions
Return Value: A Promise which will be resolved with the value returned by the async function, or rejected with an exception thrown from, or uncaught within, the async function.
Your problem here isn't that Promise doesn't handle uncaught errors, it's fundamentally because your error is asynchronous: As far as the Promise is concerned, its executor function is a successful little function that calls setTimeout. By the time your setTimeout handler runs and fails, it does so with its own stack that is unrelated to the Promise object or its function; nothing related to badPromise or p exists within your setTimeout handler other than the res reference the handler includes via closure. As in the question "Handle error from setTimeout", the techniques for catching errors in setTimeout handlers all involved editing or wrapping the handler, and per the HTML spec for timers step 9.2 there is no opportunity to catch or interject an error case for the invocation of the function passed into setTimeout.
Other than editing badPromise, there's almost nothing you can do.
Alternatives:
Modify/overwrite both the Promise constructor and the setTimeout method in sequence, wrapping the Promise constructor's method to save the resolve/reject parameters and then wrapping the global setTimeout method so to wrap the setTimeout handler with the try/catch that invokes the newly-saved reject parameter. Due to the fragility of changing both global services, I strongly advise against any solutions like this.
Create a wrapper higher-order function (i.e. function that returns a function) that accepts a rejection callback and wraps the setTimeout call. This is technically an edit to badPromise, but it does encapsulate what's changing. It'd look something like this:
function rejectOnError(rej, func) {
return (...args) => {
try {
return func(...args);
} catch (e) {
rej(e);
}
};
}
async function badPromise() {
const p = new Promise((res, rej) => { // save reject
delayTimer = setTimeout(rejectOnError(rej, () => { // to use here
console.log('running timeout code...');
if (1 > 0) throw new Error('Now this is caught');
res();
}), 1000);
});
return p;
}
badPromise().catch(x => console.error(`outer: ${x}`));
console.log('bad promise initiated');
The underlying issue is that timer callbacks run as top level code and the only way to detect errors in them is to listen for global error events. Here's an example of using a global handler to detect such errors, but it has issues which I'll discuss below the code:
"use strict";
let delayTimer; // declare variable
async function badPromise() {
const p = new Promise((res) => {
let delayTimer = setTimeout(() => { // declare variable!!!
console.log('running timeout code...');
if (1 > 0) throw new Error('This is NOT caught!'); // prevents the promise from ever resolving, but may log an error message to the console
res();
}, 1000);
});
return p;
}
(async () => {
let onerror;
let errorArgs = null;
let pError = new Promise( (res, rej)=> {
onerror = (...args) => rej( args); // error handler rejects pError
window.addEventListener("error", onerror);
})
.catch( args => errorArgs = args); // Catch handler resolves with error args
// race between badPromise and global error
await Promise.race( [badPromise(), pError] );
window.removeEventListener("error", onerror); // remove global error handler
console.log("Made it here");
if( errorArgs) {
console.log(" but a global error occurred, arguments array: ", errorArgs);
}
})();
Issues
The code was written without caring what is passed to an global error handler added using addEventListener - you may get different arguments if you use window.onerror = errorHandler.
The promise race can be won by any error event that bubbles up to window in the example. It need not have been generated in the badPromise() call.
If multiple calls to badPromise are active concurrently, trapping global errors won't tell you which badPromise call errored.
Hence badPromise really is bad and needs to be handled with kid gloves. If you seriously cannot fix it you may need to ensure that you only ever have one call to it outstanding, and you are doing nothing else that might generate a global error at the same time. Whether this is possible in your case is not something I can comment on.
Alternative
A more generic alternative may be to start a timer before calling badPromise and use it to time out the pending state of the returned promise;
let timer;
let timeAllowed = 5000;
let timedOut = false;
let timeout = new Promise( res => timer = setTimeout(res, timeAllowed))
.then( timedOut = true);
await Promise.race( [badPromise(), timeout])
clearTimer( timer);
console.log( "timed out: %s", timedOut);
There may be a way to do this, but in your case I think you really want to use the reject function inside your Promise instead of throw. That's really what reject is for.
async function badPromise() {
const p = new Promise((res, reject) => {
delayTimer = setTimeout(() => {
console.log('running timeout code...');
if (1 > 0) {
reject('This is NOT caught!');
return;
}
res();
}, 1000);
});
return p;
}
(async () => {
try {
console.log('start async');
await badPromise();
console.log('Made it to the end'); // never gets here
} catch (e) {
console.error('Caught the problem...', e); // should work now
}
})();
Maybe not an answer to what you want, but you could use a pattern like this for setTimeout:
function testErrors() {
new Promise((resolve, reject) => {
setTimeout(() => resolve(), 1000);
}).then(() => {
throw Error("other bad error!");
}).catch(err => {
console.log("Catched", err);
})
}
My scenario
I used to have some node.js implementation done using callbacks but I am now refactoring my code to use Promises instead - using Q module. I have the following update() function where the inner _update() function already returns a Promise:
exports.update = function(id, template, callback) {
if (!_isValid(template)){
return callback(new Error('Invalid data', Error.INVALID_DATA));
}
_update(id, template) // this already returns a promise
.then(function() {
console.log('UPDATE was OK!');
callback();
}, function(err) {
console.log('UPDATE with ERRORs!');
callback(err);
});
};
My question
I would like to achieve something like the following:
exports.update = function(id, template) {
if (!_isValid(template)){
// how could I make it return a valid Promise Error?
return reject(new Error('Invalid data', Error.INVALID_DATA));
}
return _update(id, template) // return the promise
.done();
};
Because _update() already returns a promise, I guess changing it this way would be enough (wouldn't be?):
return _update(id, template)
.done();
And... what about if the condition inside the if-clause equals true? How could I refactor
return callback(new Error('Invalid data', BaboonError.INVALID_DATA));
to throw an error to avoid passing the callback into update() and handling that error (or what ever error could ever be returning _update())?
Also, calling update():
myModule.update(someId, someTemplate)
.then(function() { /* if the promise returned ok, let's do something */ })
.catch(function(err) { /* wish to handle errors here if there was any */});
somewhere else in my code:
if there is an error during the promise propagation - it should handle it,
or, if there wasn't an error - it should do some other things
Am I close to what I am expecting? How could I finally achieve it?
I see only two problems.
If you want to explicitly return a rejected promise with a value, you should do that with Q.reject.
Calling .done() on promise means that the promise ends there. It cannot be chained further.
So, your code would look like this
exports.update = function (id, template) {
if (!_isValid(template)) {
return Q.reject(new Error('Invalid data', Error.INVALID_DATA));
}
return _update(id, template);
};
Now, the update function just returns a promise always. Its up to the callers to attach the success or failure handlers to it.
I'm writing a background job function on Parse.com CloudCode. The job needs to call the same function (that includes a Parse.Query.each()call) several times with different parameters, and I want to chain these calls with promises. Here's what I have so far:
Parse.Cloud.job("threadAutoReminders", function(request, response) {
processThreads(parameters1).then(function() {
return processThreads(parameters2);
}).then(function() {
return processThreads(parameters3);
}).then(function() {
return processThreads(parameters4);
}).then(function() {
response.success("Success");
}, function(error) {
response.error(JSON.stringify(error));
});
});
Below is the processThreads() function:
function processThreads(parameters) {
var threadQuery = new Parse.Query("Thread");
threadQuery... // set up query using parameters
return threadQuery.each(function(thread) {
console.log("Hello");
// do something
});
}
My questions are:
Am I chaining function calls using promises correctly?
What happens in threadQuery.each() returns zero results? Will the promise chain continue with execution? I'm asking because at the moment "Hello" never gets logged..
Am I chaining function calls using promises correctly?
Yes.
What happens in threadQuery.each() returns zero results? Will the promise chain continue with execution? I'm asking because at the moment "Hello" never gets logged.
I think I'm right in saying that, if "do something" is synchronous, then zero "Hello" messages can only happen if :
an uncaught error occurs in "do something" before a would-be "Hello" is logged, or
every stage gives no results (suspect your data, your query or your expectation).
You can immunise yourself against uncaught errors by catching them. As Parse promises are not throw-safe, you need to catch them manually :
function processThreads(parameters) {
var threadQuery = new Parse.Query("Thread");
threadQuery... // set up query using parameters
return threadQuery.each(function(thread) {
console.log("Hello");
try {
doSomething(); // synchronous
} catch(e) {
//do nothing
}
});
}
That should ensure that the iteration continues and that a fulfilled promise is returned.
The following example shows as use promises inside your function using a web browser implementation.
function processThreads(parameters) {
var promise = new Promise();
var threadQuery = new Parse.Query("Thread");
threadQuery... // set up query using parameters
try {
threadQuery.each(function(thread) {
console.log("Hello");
if (condition) {
throw "Something was wrong with the thread with id " + thread.id;
}
});
} catch (e) {
promise.reject(e);
return promise;
}
promise.resolve();
return promise;
}
Implementations of promise:
Web Browser https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
jQuery https://api.jquery.com/promise/
Angular https://docs.angularjs.org/api/ng/service/$q
So, I want my first level catch to be the one that handles the error. Is there anyway to propagate my error up to that first catch?
Reference code, not working (yet):
Promise = require('./framework/libraries/bluebird.js');
function promise() {
var promise = new Promise(function(resolve, reject) {
throw('Oh no!');
});
promise.catch(function(error) {
throw(error);
});
}
try {
promise();
}
// I WANT THIS CATCH TO CATCH THE ERROR THROWN IN THE PROMISE
catch(error) {
console.log('Caught!', error);
}
You cannot use try-catch statements to handle exceptions thrown asynchronously, as the function has "returned" before any exception is thrown. You should instead use the promise.then and promise.catch methods, which represent the asynchronous equivalent of the try-catch statement. (Or use the async/await syntax noted in #Edo's answer.)
What you need to do is to return the promise, then chain another .catch to it:
function promise() {
var promise = new Promise(function(resolve, reject) {
throw('Oh no!');
});
return promise.catch(function(error) {
throw(error);
});
}
promise().catch(function(error) {
console.log('Caught!', error);
});
Promises are chainable, so if a promise rethrows an error, it will be delegated down to the next .catch.
By the way, you don't need to use parentheses around throw statements (throw a is the same as throw(a)).
With the new async/await syntax you can achieve this. Please note that at the moment of writing this is not supported by all browsers, you probably need to transpile your code with babel (or something similar).
// Because of the "async" keyword here, calling getSomeValue()
// will return a promise.
async function getSomeValue() {
if (somethingIsNotOk) {
throw new Error('uh oh');
} else {
return 'Yay!';
}
}
async function() {
try {
// "await" will wait for the promise to resolve or reject
// if it rejects, an error will be thrown, which you can
// catch with a regular try/catch block
const someValue = await getSomeValue();
doSomethingWith(someValue);
} catch (error) {
console.error(error);
}
}
No! That's completely impossible, as promises are inherently asynchronous. The try-catch clause will have finished execution when the exception is thrown (and time travel still will not have been invented).
Instead, return promises from all your functions, and hook an error handler on them.
I often find the need to ensure a Promise is returned and almost as often needing to handle a local error and then optionally rethrow it.
function doSomeWork() {
return Promise.try(function() {
return request.get(url).then(function(response) {
// ... do some specific work
});
}).catch(function(err) {
console.log("Some specific work failed", err);
throw err; // IMPORTANT! throw unless you intend to suppress the error
});
}
The benefit of this technique (Promise.try/catch) is that you start/ensure a Promise chain without the resolve/reject requirement which can easily be missed and create a debugging nightmare.
To expand on edo's answer, if you want to catch the errors of an async function that you don't want to wait for. You can add an await statement at the end of your function.
(async function() {
try {
const asyncResult = someAsyncAction();
// "await" will wait for the promise to resolve or reject
// if it rejects, an error will be thrown, which you can
// catch with a regular try/catch block
const someValue = await getSomeValue();
doSomethingWith(someValue);
await asyncResult;
} catch (error) {
console.error(error);
}
})();
If someAsyncAction fails the catch statement will handle it.
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