JavaScript Async - Promises, The Revealing Constructor Pattern |
Written by Ian Elliot | |||
Monday, 11 December 2017 | |||
Page 1 of 2 There is something mysterious inside a Promise, object that is. You may think that you have Promises mastered but do you really know how they work? The whole security of the Promise is based on the revealing constructor pattern which is useful in its own right. This is an extract from the newly published JavaScript Async: Events Callbacks, Promises & Async/Await Now Available as a Book:JavaScript AsyncYou can buy it from: Amazon Contents
The Problem With PromisesWhen we first began implementing Promises there was a big problem. It was essential to provide functions so that the code that was using the Promise to signal when it was finished could change the state of the Promise, but code that was consuming the Promise using then and catch functions was unable to change the Promise's state. That is: only the code that created the Promise should be able to set the Promise's state. The earliest solution to this problem of keeping the internal state private was to use a two-object solution. This is the solution that jQuery adopted in the very early days of Promises and it is a lesson in why it is not always good to be a first adopter. jQuery uses two similar objects, the Deferred and the Promise to implement a standard Promise. A Deferred object was used by the Promise creator to manage the Promise. The Deferred had the necessary functions to resolve and reject the Promise, and the Promise had all of the functions the consumer needed, like then. In practice it was better to have the Deferred object also having all of the functions that the Promise had and so the Deferred looked like a "super" Promise object. The Deferred object was kept private to the object that created the Promise, and was used by it to call resolve and reject methods that place the Promise in the resolved or reject state. In retrospect this is probably a mistake as it results in a confusion between what the Deferred is, and what it is used for. If you wanted to you could pass the Deferred object to the user rather than the Promise and this would allow them to change the internal state. The two object solution to keeping the resolve and reject functions private was solved by the generalization of the mechanism long used to keep methods and properties private to an object. That is, in place of a private Deferred object which has the accept and reject methods, in the Promises standard both resolve and reject are private methods of the Promise object itself. The Revealing Constructor PatternPromises use a modification on the standard way that constructors provide private methods called the revealing constructor pattern. You don’t need to understand how this works to consume or even produce Promises, but it isn’t difficult and it might well have other uses. Like all JavaScript patterns once you have seen it, it seems more than obvious. First let’s see how to create a private method – if you are sure you know how, skip to the next section. A private method is one that is created as a closure when an object is created. This is the standard method for creating a private variable accessible from within an object, but not from outside. The only difference is that the variable references a function. For example:
This is a constructor for an object with just one method, myFunction. The important part is the variable called private. This is not part of the object because it isn't declared as a property of the object. so if you try:
you will see an error that private doesn't exist. However, as private is in scope when myFunction is declared, it is available to it as a closure. That is:
does display the value of private. A private method uses the same mechanism with the small difference that the variable references a function – an inner function which is not a method of the object being constructed. This is the mechanism that the JavaScript Promise uses to make resolve and reject private methods, but with some additional twists. The private functions and variables are accessed by a function that is passed to the constructor. Let’s see how this works. Suppose you need to set a private variable to some value, but only when the constructor is used. The simplest solution is:
Now you can create an instance using:
The value of the private variable will be printed, i.e. 10, and this has been set when the constructor was used, but the variable cannot now be altered by code external to the object. This seems simple enough, but if we push the idea just a little further it gets a little bit more difficult. What happens if you want to pass a function in the constructor call that works with private members of the object being constructed? The function is intended to be executed by the constructor as the object is being created. A first attempt might be something like:
This creates a private variable set to a random value which can be discovered by accessing the private method reveal. The idea is that func is a function passed into the constructor and then executed which can make use of reveal to access the value. If you try it out by passing it a function that tries to make use of reveal:
you will see an error message generated when func is called saying the reveal is undefined. The error here is obvious, reveal is not in scope for the function passed into the constructor. Which variables a function can access is determined by where the function is declared, not where it is used. The solution to the mistake is simple enough – pass the required private members as parameters to the function. That is:
Note now that the function needs to be called within the constructor with a parameter:
Now it all works. The function passed to the constructor can call private functions as long as they are passed to it when the function is called. The general principles are:
Notice that you can just as well write:
The name of the function is just a place holder for the first parameter passed. What the function actually is only becomes fixed when the constructor calls the function. That is when it does:
This pattern is generally useful to allow external code restricted access to private functions within a constructor and of course it is how the Promise constructor allows you access to the resolve and reject functions. |
|||
Last Updated ( Monday, 11 December 2017 ) |