We learn promises based on the Ecmascript specification. Acquaintance

promise introduction



Hello. Studying JavaScript (and, in principle, any other technology), various questions always arise, the main of which is: “Why does it work this way and not otherwise?” And it is very important at this moment not only to find the answer to the question, but also the explanation received embed in a single system of already acquired knowledge. Otherwise, the orphaned information will have to be memorized or forgotten.







Learning something together helps a lot to find answers. When a student / companion asks a question about how to understand the phrase - "... the result of the previous one" falls into the next promise in the chain ... "- you involuntarily think about it ... That's what a strange thing. But you can’t say better anymore, is it really not clear? You look into the clean, slightly naive, companion’s eyes and understand - you need to say something else. It is desirable so that you do not even have to memorize. To just new information organically fit into existing human thoughts.







I will not describe what we tried, read, watched. As a result, we became interested in the ECMAScript specification. How to read and understand it is a separate conversation (maybe even a separate post). But the way promises and their behavior are described there, for the first time gave us a holistic and logically coherent understanding of this topic. What I want to share with you.







This article is for beginners. Promises in terms of the ECMAScript specification will be discussed here. I know it sounds strange, but as it is.







The promise object: its philosophy, technical presentation, possible states



It has been repeatedly noted by me that high-quality programming training should consist of 2 parts. This is a philosophical understanding of the idea, and only then its technical implementation. That is, ordinary human logic, which the student is guided by when making any decisions, greatly facilitates the understanding of the technical implementation of this decision. Therefore, we begin with what a promise is in life, and how do we relate to it? And then we'll see: how examples of promises will be implemented in code. Consider the following figures (Fig. 1, 2, 3).







Promise state

fig 1. ([[PromiseState]] - as a result of a promise)



Promise Result

Figure 2. ([[PromiseResult]] - as information related to the result of a fulfilled or unfulfilled promise)



Promise reactions

fig 3. ([[[PromiseFulfillReactions]], [[PromiseRejectReactions]] - as the consequences that occur after the fulfillment or failure to fulfill the promise)



We see that the very concept of promise stands on 3 pillars. 1) Was the promise fulfilled at all? 2) What additional information can we extract after fulfilling or refusing a promise? 3) What are the consequences if our promise is positive or negative?







Technically, a promise is an ordinary entity expressed through a data type such as an object. This entity has a Promise name / class. Objects born from this class have Promise.prototype in their prototype chain. And this entity must somehow be connected with all the “information from life” that we examined above. The ECMAScript specification lays this information in a promise even at a level that is lower in abstraction than JavaScript itself. For example, at the level of C ++. Accordingly, in the object of the promise there is a place both under the status, and under the result, and under the consequences of the promise. Take a look at what ECMAScript's promise consists of (Figure 4).







Promise fields

Figure 4. (Internal fields of the promise object according to the ECMAScript specification)



What new colors did the phrase “promise does not mean to marry” play in terms of a programmer? 1) [[PromiseState]]. Someone has not married. 2) [[PromiseResult]]. Because he did not have enough money for the wedding. 3) [[PromiseRejectReactions]]. As a result, he got a lot of free time that he spent on self-development 4) [[PromiseFulfillReactions]]. Why does a person need plan B when he has already chosen plan A?







Yes, there is a fifth field [[PromiseIsHandled]]. It is not very important for us people, and we will no longer operate on it in the future. In short: there is stored a signal to the interpreter about whether the programmer rejected / rejected the promise or not. If not, the raw promise reject is interpreted by the JS engine as an error. For impatient ones: if the programmer did not hang the Promise.prototype.then () function as the callback handler of the rejected promise status, then the “rejected” promise state of the object will show you a red error in the developer's console.







Have you noticed that the fields of the promise object are enclosed in "[[" and "]]"? This emphasizes that the JS programmer does not have direct access to this information. Only through special tools / commands / API, such as the Promise.prototype.then () command. If you have an irresistible desire to manage “this kitchen” directly, then welcome to the EcmaScript specification standards club.







A short remark at the end of this sub-chapter. If in life we ​​can have promises partially fulfilled, then in EcmaScript - no. That is, if a person promised to give a million, but gave 950 thousand, then in life he may be a reliable partner, but for JavaScript such a debtor will be blacklisted through [[PromiseState]] === “rejected”. A Promise object changes its state unambiguously and only once. How this is technically implemented is a little later.







Designer Promise, his philosophy. The callback function executor is like the “executor” of a promise. Interaction scheme: Promise (constructor) - executor (callback) - promise (object)



So, we found out that promise is an entity that is technically a JS object with special hidden internal fields, which in turn provide philosophical filling with the meaning of the word “promise”.







When a newcomer creates a promise object for the first time, the following picture awaits him (Fig. 5).







wrong creating promise object

Figure 5. (The very first time we intuitively create a promise object)



What went wrong and why the error is a standard question. When answering it, it is better to bring some analogy from life again. For example, few people like "empty chimes" around us: who only promise, but do nothing to fulfill their statements (politics does not count). We are much better at those people who, after their promise, have a plan and immediately take some action to achieve the promised result.







So the ECMAScript philosophy implies that if you create a promise, then immediately indicate how you will fulfill it. The programmer needs to draw up his plan of action in the form of a function parameter, which you pass to the Promise constructor. The next experiment looks like this (Fig. 6).







Promise constructor uses executor

Figure 6. (Create a promise object by passing the executor function to the Promise constructor)



From the caption to the figure, we see that the function (Promise constructor parameter) has its own name - executor. Her task is to start fulfilling the promise and, preferably, bring it to some kind of logical conclusion. And if the programmer can write any code in the executor, then how can the programmer signal to JS that everything is done - can you go and see the results of the promise?







Markers or signals that help the programmer to inform that the promise has been completed are passed automatically to the executor parameters in the form of arguments specially formed by JavaScript. These parameters can be called as you like, but most often you will meet them under names such as res and rej. In the ECMAScript specification, their full name is resolve function and reject function. These function markers have their own characteristics, which we will consider a little later.







To understand the new information, the newcomer is invited to independently encode the following statement: "I promise that I can divide one number into another and give an answer, if only the divisor is not zero." Here's what the code would look like (fig. 7).







promise task: division by zero

Figure 7. (Solution of the problem of dividing 2 numbers through promises)



Now you can analyze the result. We see that for the second time the browser console shows the Promis object in an interesting way. Namely: 2 additional fields are indicated in double square brackets. You can safely draw an analogy between [[PromiseState]] and [[PromiseStatus]], fulfilled and resolved, [[PromiseValue]] and [[PromiseResult]]. Yes, the browser itself tries to tell the programmer about the presence and value of the internal fields of the promise object. We also see the connected system of the promise object, the executor function, the special callback function-markers res and rej.







In order for the student / partner to become more relaxed in this material, he is offered the following code (Fig. 8). It is necessary to analyze it and answer the following questions.







promise task: division by zero. Alternative version

Figure 8. (Variation of the solution to the problem of dividing 2 numbers through promises)



Will the code work? Where is the executor function here and what is its name? Is the name "wantToDivide" appropriate in this code? What does the bind function return after itself? Why are arguments passed to the bind function only in second and third place? Where did the special functions resolve function and reject function disappear? How did the necessary input numbers number1 and number2 get into the "promise fulfillment plan"? How many elements are in the arguments pseudo-array? Is it possible to recover from memory how the answer will look in the browser console?







The reader is invited to think about the answers to the questions himself. And

experiment in code. Fortunately, the code is small and the idea of ​​the task is simple. Yes, there are questions about both promises and general knowledge of JavaScript. What to do, everywhere we are waiting for surprises that prevent us from relaxing. As soon as everything becomes clear to you, you can move on.







View / copy code
let number1 = Number(prompt("input number 1")); let number2 = Number(prompt("input number 2")); let wantToDivide = function() { if (arguments[1] === 0) { arguments[3]("it is forbidden to divide by zero"); return; } let result = arguments[0] / arguments[1]; arguments[2](result); }; let myPromise = new Promise(wantToDivide.bind(null, number1, number2)); console.log(myPromise);
      
      







Consider executor-a arguments: resolve and reject functions



So, we had a coffee - we move on. Let us consider in more detail the special functions resolve function and reject function, which are automatically generated by JavaScript to translate the promise of the object into the fulfilled or rejected state, which symbolizes the end of the promise.







For starters, let's try to look at them simply in the developer's console (Fig. 9).







research resolve function

Figure 9. (Investigation of the function resolve function - res)



We see that the resolve function is a function that takes one argument (property length === 1). And its prototype is Function.prototype.







Ok, let's continue the experiments. And what will happen if we remove the link to the resolve / reject function from the executor to the external scope? Will something break (fig. 10)?







external contol of promise object

Figure 10. (We translate myPromise promise into a fulfilled state outside the promise)



Nothing unusual. Functions as a subspecies of an object in JavaScript are passed by reference. Everything worked out as we expected. The variable from the outerRes closure got a reference to our resolving function res. And we used its functionality to put the promise in the fulfilled state outside the executor itself. The following slightly modified example shows the same thought, so look at the code and think about what state and what value myPromise1 and myPromise2 will be in (Fig. 11). Then you can check your assumptions under the spoiler.







promise task. Question Figure 11. (The task of reflection. In what state and with what value will the promises myPromise1 and myPromise2 be in the developer's console?)



The answer to the problem in Figure 11 (Figure 12).
promise task. Answer

Figure 12. (The answer to the problem in Figure 11)



And now you can think about one interesting question. But how does the resolve / reject function always know exactly which promise to translate the object into the necessary state? We turn to the algorithm in the specification , which describes how these functions are created (Fig. 13).







create resolving functions

Figure 13. (Features of creating resolving functions for one specific promise object)



Important points to pay attention to:










All Articles