preface
Learning is a long process, step by step, in order to deeply understand a technology, you need to sink down and study slowly. To see the essence through the phenomenon, learning Promise will also use higher-order functions and publish and subscribe, only by mastering those general technologies can we learn more effectively and see the essence better. Over time, you’ll find that by studying a Promise in depth, you’ll get more than just a Promise.
What is Promise
Promises are a solution to asynchronous programming, and the Promise object is a constructor that generates a Promise instance.
Ii. Promise objects have the following two characteristics.
Only the result of the asynchronous operation can determine which state is currently pending. No other operation can change this state
Once the state changes, it will never change again. There are only two possibilities for the state change of the Promise object: from pending to depressing and from pending to Rejected. As long as those two things happen, the state is frozen, it’s not going to change, it’s going to stay the same
Iii. Realization of Promise (ES5)
1.
When new Promsie(executor) is used, executor is executed as synchronized code immediately.
This is a big pity. Status: Three states pending, fulfilled and Rejected of the Promise instance
3. Value: This is the parameter passed successfully. Success has a success value.
4. Reason: This parameter is passed when failure occurs. Failure has a reason for failure.
Resolve: A function that is called on success and passed to executor as an argument
Reject: A higher-order function that is called on failure and passed to executor as an argument
2. The most basic Promise:
The most basic promise source code only 34 lines, read the 34 lines of code, you will understand the most core part of the promise, is not very excited!!
Code read: In Promise, define the status, success value, and reason for failure when you pass infunction(resolve,reject){resolve('It worked'/ / Resolve will be triggered as a callback function, and the previously defined state pending will become a pitythenThe method passes in two callbacks, which are compared internallyifThis is onFulfilled or onRejected.function Promise(executor){
const self = this;
self.status = 'pending'; // Promise state self.value = undefined; Self. reason = undefined; // There are reasons for failurefunction resolve(value){
if(self.status === 'pending'){// Only pending states can be changed self.status ='fulfilled'; self.value = value; }}function reject(reson){
if(self.status === 'pending'){// Only pending states can be changed self.status ='rejected'; self.reason = reson; } } try{ executor(resolve,reject); }catch(err){reject(err);}catch(err){reject(err); } } Promise.prototype.then =function(onFulfilled,onRejected){
const self = this;
if(self.status === 'fulfilled'){
onFulfilled(self.value);
}
if(self.status === 'rejected'){ onRejected(self.reason); }}Copy the code
The above code can normally run the following code:
const p = new Promise((resolve,reject)=>{
resolve('success')
// throw 1213
// reject('failure')}); p.then(function(data){console.log(data) // success},function(err){console.log(err) // failed or 1213})Copy the code
At this point, the basic Promise has been wrapped, isn’t it?
3, how to solve the asynchronous call?
The code encapsulated above has no result if it encounters the code below.
synchronous
The code is executed in the following order:executor ---> resolve ---> then
asynchronous
The code is executed in the following order:executor ---> then ---> resolve
So when you call then, remember to look at the implementation of the THEN method. Its internal status is pending. Remember that the implementation of the then method has a success or failure state. This is a big pity, which is a big pity. This is a big pity, which is a big pity. This is a big pity, which is a big pity. OnRejectedCallbacks install the then callback onRejected (resolve)
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('I am asynchronous success')})});Copy the code
To solve the asynchronous update code is as follows:
function Promise(executor){ const self = this; self.status = 'pending'; // Promise state self.value = undefined; Self. reason = undefined; // There are reasons for failure+ self.onResolvedCallbacks = [];
+ self.onRejectedCallbacks = [];
function resolve(value){
if(self.status === 'pending'){// Only pending states can be changed
self.status = 'fulfilled';
self.value = value;
+ self.onResolvedCallbacks.forEach(item => item(value));
}
}
function reject(reson){
if(self.status === 'pending'){// Only pending states can be changed
self.status = 'rejected';
self.reason = reson;
+ self.onRejectedCallbacks.forEach(item => item(value));} } try{ executor(resolve,reject); }catch(err){reject(err);}catch(err){reject(err); } } Promise.prototype.then = function(onFulfilled,onRejected){ const self = this; if(self.status=== 'fulfilled'){
onFulfilled(self.value);
}
if(self.status === 'rejected'){
onRejected(self.reason);
}
+ if (self.status == 'pending') {
+ self.onResolvedCallbacks.push(function (value) {
+ try {
+ onFulfilled(value);
+ } catch (e) {
+ reject(e);
+}
+});
+ self.onRejectedCallbacks.push(function (value) {
+ try {
+ onRejected(value);
+ } catch (e) {
+ reject(e);
+}
+});
+}
+}
Copy the code
4, Promise/A+ complete implementation
The resolvePromisefang method resolves those things:
What is a resolvePromise? : This is actually an official Promise/A+ requirement. Because your then can return any job, including a Promise, of course, and if it’s a Promise, we need to unpack it until it’s not a Promise and take its value.
function Promise(executor) {
let self = this;
self.status = "pending";
self.value = undefined;
self.onResolvedCallbacks = [];
self.onRejectedCallbacks = [];
function resolve(value) {
+ if (value instanceof Promise) {
+ return value.then(resolve, reject)
+}
+ setTimeout(function () {// Execute all callbacks asynchronouslyif (self.status == 'pending') { self.value = value; self.status = 'resolved'; self.onResolvedCallbacks.forEach(item => item(value)); }});+}
function reject(value) {
+ setTimeout(function () {if (self.status == 'pending') { self.value = value; self.status = 'rejected'; self.onRejectedCallbacks.forEach(item => item(value)); }});+}try { executor(resolve, reject); } catch (e) { reject(e); }}+ function resolvePromise(promise2, x, resolve, reject) {
+ if (promise2 === x) {
+ return Reject (new TypeError(' circular reference '));
+}
+ let then, called;
+ if (x != null && ((typeof x == 'object' || typeof x == 'function'))) {
+ try {
+ then = x.then;
+ if (typeof then == 'function') {
+ then.call(x, function (y) {
+ if (called)return;
+ called = true;
+ resolvePromise(promise2, y, resolve, reject);
+ }, function (r) {
+ if (called)return;
+ called = true;
+ reject(r);
+});
+ } else {
+ resolve(x);
+}
+ } catch (e) {
+ if (called)return;
+ called = true;
+ reject(e);
+}
+ } else {
+ resolve(x);
+}
+}
Promise.prototype.then = function (onFulfilled, onRejected) {
let self = this;
+ onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : function (value) {
+ return value
+};
+ onRejected = typeof onRejected == 'function' ? onRejected : function (value) {
+ throw value
+};
+ let promise2;
if (self.status == 'resolved') {
+ promise2 = new Promise(function (resolve, reject) {
+ setTimeout(function () {
+ try {
+ let x = onFulfilled(self.value);
+ resolvePromise(promise2, x, resolve, reject);
+ } catch (e) {
+ reject(e);
+}
+});
+});
}
if (self.status == 'rejected') {
+ promise2 = new Promise(function (resolve, reject) {
+ setTimeout(function () {
+ try {
+ let x = onRejected(self.value);
+ resolvePromise(promise2, x, resolve, reject);
+ } catch (e) {
+ reject(e);
+}
+});
+});
}
if (self.status == 'pending') {
+ promise2 = new Promise(function (resolve, reject) {
+ self.onResolvedCallbacks.push(function (value) {
+ try {
+ let x = onFulfilled(value);
+ resolvePromise(promise2, x, resolve, reject);
+ } catch (e) {
+ reject(e);
+}
+});
+ self.onRejectedCallbacks.push(function (value) {
+ try {
+ let x = onRejected(value);
+ resolvePromise(promise2, x, resolve, reject);
+ } catch (e) {
+ reject(e);
+}
+});
+});
+}
+ return promise2;
}
Copy the code
Iv. Implementation of other methods of Promise
all:
The promise.all () method takes an array of arguments. P1, p2, and p3 are all Promise instances. If they are not, the Promise. In addition, the promise.all () method can take arguments that are not arrays, but must have an Iterator interface and return each member as a Promise instance
Promise.all=function(promises){
return new Promise((resolve,reject)=>{
promises=Array.from(promises);
const length=promises.length, results=[],_index=0;
if(length==0){
resolve(results)
return
}
promises.forEach((promise,index)=>{
Promise.resolve(promise).then(result=>{
results[index]=result;
if(length==++_index){
resolve(results)
}
}).catch(err=>{
reject(err)
})
})
})
}
Copy the code
race
const p = Promise.race([p1, p2, p3]);
In the above code, the state of P changes as long as one of the first instances of P1, P2, and P3 changes state. The return value of the first changed Promise instance is passed to p’s callback.
Promise.race=function(promises){
return new Promise((resolve,reject)=>{
promises=Array.from(promises);
if(promises.length==0){
resolve()
return
}
promises.forEach((promise,index)=>{
Promise.resolve(promise).then(result=>{
resolve(result)
}).catch(err=>{
reject(err)
})
})
})
}
Copy the code
finally
The finally method is used to specify actions that will be performed regardless of the final state of the Promise object. This method was introduced as a standard in ES2018.
Promise. Then (result = > {...}). The catch (error = > {...}), finally (() = > {...});Copy the code
In the code above, regardless of the last state of the promise, the callback specified by the finally method is executed after the callback specified by then or catch.
Promise.prototype.finally=function(onFinally){
var isFunction = typeof onFinally == "function";
returnthis.then(isFunction? (result)=>{ onFinally();returnresult }:onFinally,isFunction? (err)=>{ onFinally();return Promise.reject(err)
}:onFinally);
}
Copy the code
resolve/reject
Resolve: Sometimes you need to turn an existing object into a Promise object, which the promise.resolve () method does
Reject: promise. reject(Reason) also returns a new Promise instance with a state of Rejected.
// the resolve method promise.resolve =function(val){
returnNew Promise((resolve,reject)=>{resolve(val)})} //reject promise.reject =function(val){
return new Promise((resolve,reject)=>{
reject(val)
})
}
Copy the code