The essential
Basic preheating: Hello, JavaScript asynchronous programming —- understand the beauty of JavaScript asynchronous
Understanding the Beauty of Asynchrony: Promise and Async await
After the last basic Promise explanation, I think we have a certain understanding of the basic use and idea of Promise. (It’s a promise)
Let’s take a look at how it works
Combine source code with analysis of other people’s common implementations to understand
Below is the total source code implemented by others, (a simple look can be)
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
function Promise(callback) {
this.status = PENDING;
this.value = null;
this.defferd = [];
setTimeout(callback.bind(this.this.resolve.bind(this), this.reject.bind(this)), 0);
}
Promise.prototype = {
constructor: Promise.resolve: function (result) {
this.status = FULFILLED;
this.value = result;
this.done();
},
reject: function (error) {
this.status = REJECTED;
this.value = error;
},
handle: function (fn) {
if(! fn) {return;
}
var value = this.value;
var t = this.status;
var p;
if (t == PENDING) {
this.defferd.push(fn);
} else {
if (t == FULFILLED && typeof fn.onfulfiled == 'function') {
p = fn.onfulfiled(value);
}
if (t == REJECTED && typeof fn.onrejected == 'function') {
p = fn.onrejected(value);
}
var promise = fn.promise;
if (promise) {
if (p && p.constructor == Promise) {
p.defferd = promise.defferd;
} else {
p = this;
p.defferd = promise.defferd;
this.done(); }}}},done: function () {
var status = this.status;
if (status == PENDING) {
return;
}
var defferd = this.defferd;
for (var i = 0; i < defferd.length; i++) {
this.handle(defferd[i]); }},then: function (success, fail) {
var o = {
onfulfiled: success,
onrejected: fail
};
var status = this.status;
o.promise = new this.constructor(function () {});
if (status == PENDING) {
this.defferd.push(o);
} else if (status == FULFILLED || status == REJECTED) {
this.handle(o);
}
returno.promise; }};Copy the code
This is a source code implementation of a common Promise on the web and I’ll do an analysis of this (why not implement one yourself? Solution: time saving, too much on the Internet, the essence is still to understand ideas.
So let’s get started
Let’s start with a general overview of the implementation of what to do?
First:
let promsie = new Promise((resolve,reject) = >{doSomething()}) with this constructor, we need to implement two methods, resolve and reject.Copy the code
second :
promise.then(res= >{ doSomethingRes(); },rej=>{doSomenthingRej()}) we want to implement a then method that can execute different functions according to different states.Copy the code
The two most basic things we’ve identified.
Without further ado, start analyzing the code.
Analysis of the code in the content of the annotations we do not omit ha!!
The first constructor and state setting
// First declare three states,
// State is what we said in the previous section: things are in progress, done, and failed.
// These three states follow PENDING->FULEFILLED or PENDING->REJECTED
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
// The Promise constructor takes u a callback
function Promise(callback) {
// The status of new instances must be PENDING.
this.status = PENDING;
// Value is the value that you save internally when something is done or failed
Resolve (42) then res=>{dosomething () res is 42
}
this.value = null;
// defferd, which literally means deferred, is an array of events that this promise will execute later
// Analog publish-subscribe mode (observer mode). Holds a list of events that the observer subscribes to on the observed
// Events in defferd hold events to be executed by future observers.
this.defferd = [];
// setTimeout asynchronously executes a new promise instance task without blocking the main thread.
// The new Promise instance does not execute asynchronously.
// It is not reasonable to choose asynchronous.
// Bind. How to specify the parameters for callback? Use the bind method to convert a function called Currying (Currying is as easy to remember as an NBA star name)
// And bind the function's this execution. Each of these in the function executes the promise instance that comes out of this new
Resolve and reject are mandatory as callback arguments.
// So we write a callback that names its arguments in the resolve and reject methods
setTimeout(callback.bind(this.this.resolve.bind(this), this.reject.bind(this)), 0);
}
Copy the code
Self-implementation versus official Promise implementation. You can see the execution order problem caused by setTimeout. So learn to look at other people’s implementations of various features in comparison.
So now we’ve seen what we do when we create a new constructor.
1: Define a state for the Promise instance with a value of PENDING.
2: Define a value space for the Promise instance.
3: Set up a publish list and publish its events at a later specified time.
4: The bind function is used to currify the callback, so that the corresponding resolve and reject methods are called when the callback is executed, and the callback is executed
The second part analyzes the Resolve Reject THEN method
Why these three methods first. Resolve and reject are core methods, and they are closely related to each other because they must be done by the THEN method.
// Specify these methods on the Promise prototype object.
// This approach has major drawbacks, and the Promise source code does not do this
Promise.prototype = {
// Overriding a prototype object specifying a Promise causes the constructor property to be lost
// Fill in here, manually specifying the constructor property on the Promise prototype object
constructorResolve: function (result) {: Promise, // resolve: function (result) {// Change the status to depressing.
this.status = FULFILLED;
// Store the result in the space mentioned in the previous constructor
this.value = result;
// done method. Indicates that the execution is complete (will continue later)
this.done();
},
// This is similar to the resolve method
reject: function (error) {
// State changes
this.status = REJECTED;
this.value = error;
// There is no done function.
},
// Start with the then method
// Success indicates the function to be performed when the state becomes a pity
// fail indicates the function to be executed when the state changes to REJECTED
then: function (success, fail) {
// Declare an object to hold these events.
var o = {
onfulfiled: success,
onrejected: fail
};
// Get the state of the current promise.
// The implication of this is that there are two cases when we execute the THEN method on a Promise instance
// The contents of the promise instance have not been completed. Two: The promise instance content has completed and the state has changed. Moving on.
var status = this.status;
o.promise = new this.constructor(function () {});
// If the state is PENDING, the instance content has not finished executing.
The then method specifies that events to be executed in a certain state are future events and are not executed now.
// So put o in the defferd subscription list.
// The content in defferd is executed when a condition is triggered.
// So put future methods into the subscription list when the promise instance content is not yet complete
if (status == PENDING) {
this.defferd.push(o);
// Corresponding to case 2 mentioned earlier
// What if the state becomes one of the following?
// There should be no current o in the subscription list.
// Therefore, the current specified event is executed immediately and will not be executed in any future case.
// So it will not be put into defferd
} else if (status == FULFILLED || status == REJECTED) {
// Execute the handle function
this.handle(o);
}
// The then method has chained calls. The return value of the THEN method must be a Promise object
returno.promise; }};Copy the code
Here’s a general overview of the use of resolve, Reject, and THEN methods.
A reject method that does not execute done results in the following
A callback that executes in a New Promise instance must be called resolve or reject (or both). When the resolve method is called, the state of the promise is changed to store the result. To complete the task, run the done function. (Reject doesn’t do it again)
The execution events of the THEN method have no precedence over the resolve method. It doesn’t matter who comes first. Register the event to be executed before resolve (reject). Execute after resolve (reject) and do not register.
How exactly is paragraph 3 implemented? Talk about done and Handle
Class, in the order the code is executed, we should look at the done method first. And then I’m going to look at handle so I’m going to go down a little bit and find the lovely done method.
// If you have seen the done method, you will be annoyed by it. hahahaha
// If you haven't seen the done method, go back and look.
// What is the o? O is the object in the DEfferd array passed to the then method.
// What is handle for?? That's used to perform o. O contains what we want to implement
// Where else is handle executed? Remember, when the then method executes
// If the state has changed. So just handle (O) and do what you need to do.
handle: function (fn) {
// o does not exist? Damn, what do we do? There's nothing in defferd. All right, do nothing
if(! fn) {return;
}
var value = this.value;
var t = this.status;
var p;
// If the status is PENDING, o is not yet ready for execution. So unenforceable?
if (t == PENDING) {
this.defferd.push(fn);
} else {
// This will be a big pity.
// This is a big pity. // And the things you will do during the state will be FULFILLED gradually.
// Execute. Then method res=>{doSometthing(res)}
// This res is the value stored in the promise.
if (t == FULFILLED && typeof fn.onfulfiled == 'function') {
p = fn.onfulfiled(value);
}
// Not to mention.
if (t == REJECTED && typeof fn.onrejected == 'function') {
p = fn.onrejected(value);
}
// But what does this p do ????
// Store the return value of the method for chained calls. What method implements the chain call?
// Return O.Plomise. Return O.plomise does not exist? (Of course this is not possible)
// There is no chain call.
// There is a method in a promise that when an event occurs within the then function (res=>{})
// If the return value is a Promise object, then the return value is the Promise object
// The next "then" in the chain waits for the promise to complete.
// What if it is not a Promise object? Then do the registration thing with the then method behind the chain.
var promise = fn.promise;
if (promise) {
// If the event you registered returns a Promise object
if (p && p.constructor == Promise) {
// The current p (which is a Promise object) can take events from the subscription list of the O.Plomise object.
// After serialization, the returned promise continues to control the contents of the defferd.
// In theory, each promise object should have only one defferd value.
// Since the serial chain each THEN registered event is in the defferd of the O.Pomise returned by the previous THEN.
// Then why? Defferd to write an array? That's where I'm confused but it doesn't matter
p.defferd = promise.defferd;
} else {
// What if it's not promise? Use the current promise object as your return value
// Continue to inherit the defferd from O.Plomise
p = this;
p.defferd = promise.defferd;
// There is no commitment, p should be in a resolved (reject) state
// Execute the done method directly.
this.done(); }}}},// The done method is executed in reject (reject), indicating that the resolve method is complete.
// This is a definite signal, which is a big pity that I will do those things I said before.
// Folks, brothers, we can be executed
done: function () {
// To be sure, PENDING status is not executed.
// The done function is not called PENDING. Double insurance
var status = this.status;
if (status == PENDING) {
return;
}
// Where are you? It's in your defferd. It's time for the guys on our subscription list to execute.
// Walk through our defferd objects below, don't forget to execute them all.
var defferd = this.defferd;
for (var i = 0; i < defferd.length; i++) {
// It's not a function, it's an object.
// Why objects? In the then method there is defferD.push (o); O what? It's an object.
// how to execute ???? You need a specific method to execute O, which is handle.
// Now you can go back a little bit.
this.handle(defferd[i]); }},Copy the code
I have covered the handle and done methods in this paragraph. Mainly for chain calls. That’s why I designed it like this. So chain calls are still a very popular feature. You can also try to implement the promise function in line with the Promise specification.
Pro! Think after learning
I don’t know how you feel about the common promise source code implementation on the Internet.
Let me start with my feelings
Read the source code (sorry my IQ is limited, in a short time is really do not understand ah), feel the source code to do too reasonable. It makes sense to me if I don’t understand… It’s not an excessive worship of the strong, but it’s really reasonable. Common implementation on the Internet, only the implementation of the function, such a promise is only suitable for a certain promise experience and follow the rules of the people. Why do you say that??
One: In this way, the promise state can be artificially changed at any time, exposed to the outside world, and not set as a private attribute.
Two: for convenience, choose to set methods on the prototype chain, so that private variables cannot be used.
Reject is not executed properly, just resolve is used properly.
Even though I’m saying this, I can’t do it, and the people who wrote it are way better than me
The source code for Promise was (version number, I don’t remember)
Put the resolve, reject, all, race, and handle methods inside the constructor.
Put catch, then, chain methods on the prototype.
After comparing the source code, I feel that although I generally understand the process, but this kind of precision and elegant way is difficult to master in a short time. Promise source, of course, will stick to it, the Internet can put the promise in accordance with the specification to achieve again has been very powerful. Although I think there are still places to modify, but I am far worse than them (this feeling is a bit like: I don’t, I will compare), to learn from them, according to their efforts.
Stop drinking chicken soup
The front end of the learning road is still very long, I read (just read) source code half hand are all over the number. Or believe that if you stick to it, you’ll be great. Everyone learned from control flow statements, logic is just complex control flow (also involves high-end algorithms and design patterns), I believe I can succeed!! Work together on every front-end ER (Boy and Girl). So all source level do not understand, do not understand can be attributed to see less, think less, understand less. (It has nothing to do with your IQ.)
Next up
The next chapter is the end of understanding the beauty of asynchrony. The beauty of asynchrony is this magical idea. Grab the tail of the mind, not tied down by technology, hey, hey, hey.
It’s time to start a new project. The topic should be around the vue-Router source code for learning. A periodic article that shares the learning process. Look forward to it!