|Written by Ian Elliot|
|Monday, 01 October 2018|
Page 1 of 4
In this chapter we are going to look at using Promises to create asynchronous code that is easy to understand and hard for bugs to hide in. Specifically we are going to look at how to use the Promises that other functions return in place of callbacks.
Now Available as a Book:
You can buy it from: Amazon
Promises are a way of organizing asynchronous calls that is better than using callbacks. The callbacks are still there, but they come with a degree of organization. Promises are also the basis for the next level of making async code easier to write – async and await.
In this chapter we are going to be looking at consuming Promises.
In the next chapter we will look at how to add Promise support to your own asynchronous code i.e. how to produce Promises for others to use.
What is the problem?
The usual solution to this problem as explained in earlier chapters is to use a callback function. The callback is passed to the function that is going to do the long job and instead of keeping the thread waiting it simply returns immediately. This allows the thread to do other work while it get on with its task. When it has finished it calls the callback function, usually with the result of the task. The callback function then processes the results.
Callbacks are difficult because they alter the flow of control in an unnatural way and this has been explained in an earlier chapter. However, it is worth saying that the precise problem that Promises were introduced to solve is that of running asynchronous tasks one after the other.
That is, if you have three asynchronous tasks and simply call them:
TaskA(); TaskB(); TaskC();
Then they will execute in an order that depends on how long each takes. They effectively run in parallel.
If you want them to run sequentially – that is TaskB only starts after TaskA ends, and TaskC starts after TaskB ends, then you have to use some sort of mechanisms to signal the end of each task and initiate the next one in the sequence. We looked at some ways of doing this in Chapter 3, but the most basic is to use callbacks.
The callback solution is to use nested callbacks. Something like:
where each task accepts a callback that is invoked when it ends. This looks simple enough in this example, but this is because it is over simplified. In real life nested callbacks quickly degenerate into "callback hell" and there is no standard way of handling errors except for having a success and a failure callback for each function.
Running asynchronous tasks sequentially is something Promises make easy.
Promises are an approach to the whole problem of working with asynchronous functions and as such they are worth knowing about.
Let's look first at the basic operation of a Promise.
The Basic Promise
An operation that takes some time will generally return a Promise object at once and then complete its task in the background:
Notice that even though you get a Promise object returned at once, it is unlikely that slowFun has finished whatever it is trying to do when the program moves on to the next instruction. Indeed the whole program might have come to an end before slowFun finishes.
There is also the idea that the slow function is computing a value which your program is waiting for – this isn't necessarily the case as you could be just waiting for it to complete what it is doing. However, in most cases the Promise is regarded as being a Promise to deliver a future value and this view is often helpful.
What do you do with a Promise?
The most common thing to do is to use its then method to set up what should happen when the slow function is complete:
where onComplete is a function that is called when the slow function finishes its task. It is passed the value that the slow function generates on completion. That is, when the Promise is fulfilled it supplies the value to the onComplete. The onError function is optional and is called if an error occurs while the slow function is executing. The value that the onError receives is the reason for the error.
Both the onComplete and onError functions are optional.
A Promise object is in one of three states.
A Promise is also said to be settled if it isn't pending i.e. if it is either fulfilled or rejected.
Once a Promise is settled its state and value don't change.
It is important to realize that there is no rush to use the then method, or any other method to define the functions to be called. The Promise object's state determines if and when any of the functions are called. If a Promise is already settled when you add functions as part of a then method they will still be carried out.
The key idea is that a Promise always activates the onComplete or onError after the code that is currently running has finished.
That is, a Promise always executes its functions asynchronously.
Understanding of this is essential to your understanding and use of Promises. You can be assured that nothing will happen as the result of you creating or modifying a Promise until the current thread of execution has been released. In particular, no onComplete or onError functions will be called, and as a result there is no possibility of any "race" conditions occurring. The Promise will resolve and call the functions you have specified perhaps long after your code has performed a return.
In particular, you can add as many onComplete or onError functions as you want to. For example:
When the Promise is settled, the handlers are not necessarily called in the order that they were added.
|Last Updated ( Monday, 01 October 2018 )|