|Managing Asynchronous Code - Callbacks, Promises & Async/Await|
|Written by Mike James|
|Monday, 21 March 2022|
Page 2 of 4
The Callback And The Closure
The callback is the most common approach to asynchronous code but it distorts the flow of control and potentially loses context.
For example when you convert the asynchronous call to a callback all of the code that follow it - the "after" code - becomes the callback:
You can now see that the flow of control has been distorted - what was one function is now two. Don't worry too much about it at the moment but this is a complete picture of what can happen because if the Callback contains an asynchronous call you repeat the procedure of moving the "after" code into a callback - distorting the flow of control again.
Not only that but in a simple programming environment the context is lost. As the "after" code is now a separate function it no longer has access to the variables contained in the "before" code. In short the callback can't perform an instruction like Text(1,i) because i isn't only out of scope it doesn't even exist.
This is the context bottle neck problem that callbacks introduce into asynchronous code. All of the data that was available to "after" now has to be sent to the callback as a set of parameters. Of course this doesn't work in practice because the LoadA function will often be a library function and you wont have control over its parameters. In other words LoadA will probably pass its result to the callback function(result) but it wont pass any variables from the context - function(result,i) say.
There are many complex and esoteric explanations of what closure is and why you might want it but it is this automatic provision of context to a callback function that seems the most convincing. There are lots of other uses of closure but it is this one that you would invent closure for.
Closures ensure that callbacks have their context.
Of course things can be more complicated. It could be that the asynchronous function is itself nested within a control structure.
This means that you not only have code before and after the asynchronous call there is code around it. This code that surrounds the call can be thought of as being part of the before and after code - but in reality it is quite a different type of problem. You have to implement something that converts the control structure into callbacks with a similar behaviour and this is not easy to solve in a completely general way. For example, one way to implement a general asynchronous loop is to convert it into a recursion - see What Is Asynchronous Programming?.
So, no computer science, no implementation details and no adding promises to existing asynchronous code. Instead we are going to assume that we live in a world where all asynchronous code uses promises and it is just up to us to code using this fact.
Instead of accepting a call back the asynchronous function returns a Promise object. This can be in three possible states:
Once a Promise object enters either the fulfilled or rejected state it doesn't change state again.
The eventual result of a promise is always a single value - which sounds restrictive until you know that it can be an object.
returns a Promise object imediately and gets on with whatever it is supposed to be doing to get the eventual result.
It is often said that a Promise is a promise to return a result.
So how do you get to process the result?
The then method of the Promise object is the most common way to process the eventual result.
When the Promise object enters the fulfilled state the functions registered with the then methods are executed with the result as their single parameter.
You can register multiple functions with a Promise object and they will be executed in the order that they were registered.
If a Promise object is in the fulfilled state when you register a function then it will be called almost immediately.
The reason why it is "almost" immediately is that all registered functions are called asynchronously and hence the function that is using the Promise has to end before any "then" functions are called.
All registered functions are called just once.
even if the asyncFunc is completed before the functions are registered.
So far the Promise looks a lot like just a neater way of setting up a callback - and in many ways that really is all it is but there are some other advantages in doing things this way.
The Promise object is returned immediately to the calling function and this can be used to set up complicated call sequences that will only happen long after the calling function has completed.
For example you could set two async operations going using:
in this case which of the two would finish first isn't determined and the messages will be printed on the console in any order.
|Last Updated ( Wednesday, 23 March 2022 )|