preface
I believe that Promise is often used, or with asynchronous solutions such as Generator, ASNYC /await, and the Promise principle is everywhere on the Internet. Always want to spare time to also write a Promise to achieve, but the usual work is also busy, just New Year’s day put 3 days holiday, rest 2 and a half days, take out half a day time to see a Promise.
How to Use Promises
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 1000)
}).then((data) => {
console.log(data);
return new Promise((res) => {
setTimeout(() => {
res(2);
},1000)
})
}).then((res) => {
console.log(res);
})
Copy the code
The term
Promise
Is one that contains compatibilitypromise
specificationthen
Method object or function.thenable
Is one that containsthen
Method object or function.value
Is anyJavascript
Value. (includingundefined
.thenable
.Promise
, etc.).exception
Is made up ofthrow
The value thrown out of the expression.reason
Is a descriptionPromise
The value of the reason for rejection.
Due to thePromise/A+
The specification does not covercatch
,race
,all
And so on method implementation, so here also won’t go to explain in detail.
requirements
A Promise must be in one of these states: pending, fulfilled or rejected. If pending, promise:
This can be fulfilled or rejected. This is very depressing. This is very depressing.
- You can’t transition to any other state.
- There must be a value and this value cannot be changed.
If the state is rejected, the promise can:
- You can’t transition to any other state.
- There must be a reason and the value cannot be changed.
function MyPromise(callback) {
let that = this;
// Define the initial state
/ / Promise
that.status = 'pending';
//value
that.value = 'undefined';
// Reason is a value that describes why a Promise was rejected.
that.reason = 'undefined';
/ / define the resolve
function resolve(value) {
// When status is pending, define the Javascript value and define its state as fulfilled
if(that.status === 'pending') {
that.value = value;
that.status = 'resolved'; }}/ / define reject
function reject(reason) {
// If status is pending, define the reason value and the status is rejected
if(that.status === 'pending') {
that.reason = reason;
that.status = 'rejected'; }}// Catch whether callback reports an error
try {
callback(resolve, reject);
} catch(error) { reject(error); }}Copy the code
A Promise object has a THEN method that registers a callback after the Promise state is determined. The THEN method takes two arguments: promise.then (onFulfilled,onRejected). Let’s write the then function on the prototype.
MyPromise.prototype.then = function(onFulfilled, onRejected) {
let that = this;
if(that.status === 'resolved') {
onFulfilled(that.value);
}
if(that.status === 'rejected') { onRejected(that.reason); }}Copy the code
The above code only implements the most basic logic of Promise. If you call THEN directly, it can be executed, but it does not support async. The most important feature of Promise is to solve the problem of asynchronous callback hell. So let’s transform it.
function MyPromise(callback) {
let that = this;
// Define the initial state
/ / Promise
that.status = 'pending';
//value
that.value = 'undefined';
// Reason is a value that describes why a Promise was rejected.
that.reason = 'undefined';
// The array used to solve the asynchronous problem
that.onFullfilledArray = [];
that.onRejectedArray = [];
/ / define the resolve
function resolve(value) {
// When status is pending, define the Javascript value and define its state as fulfilled
if(that.status === 'pending') {
that.value = value;
that.status = 'resolved';
that.onFullfilledArray.forEach((func) = >{ func(that.value); }); }}/ / define reject
function reject(reason) {
// If status is pending, define the reason value and the status is rejected
if(that.status === 'pending') {
that.reason = reason;
that.status = 'rejected';
that.onRejectedArray.forEach((func) = >{ func(that.reason); }); }}// Catch whether callback reports an error
try {
callback(resolve, reject);
} catch(error) { reject(error); }}Copy the code
Modification of the then function
MyPromise.prototype.then = function(onFulfilled, onRejected) {
let that = this;
This is a pity pity. This is a pity pity. This is a pity pity.
// We have two arrays of on☆ darray
if(that.status === 'pending') {
that.onFullfilledArray.push((value) = > {
onFulfilled(value);
});
that.onRejectedArray.push((reason) = > {
onRejected(reason);
});
}
if(that.status === 'resolved') {
onFulfilled(that.value);
}
if(that.status === 'rejected') { onRejected(that.reason); }}Copy the code
Since the Promise/A+ specification states that A Promise must be in either of the following states: This is very depressing. This is very depressing, which is very depressing. This is very depressing, which is very depressing. Therefore, when we initialize the Promise, we define two arrays onFullfilledArray and onRejectedArray to hold the two callback functions onFulfilled and onRejected. At the same time, we will determine whether status is pending in the THEN function, and then pass onFulfilled and onRejected into the corresponding array respectively. When the user calls resolve or Reject, it changes the state, traverses the group, and executes onFulfilled or onRejected, thus making the call asynchronously.
A chained call to the then function
In the Promise/A+ specification:
For a promise, its THEN method can be called multiple times
- when
promise
fulfilled
After allonFulfilled
Must be executed in the order in which they were registered. - when
promise
rejected
After allOnRejected
Must be executed in the order in which they were registered.
Then must return a promise
- if
onFulfilled
或onRejected
Returns the valuex
, the implementationPromise
Analytical process[[Resolve]](promise2, x)
. - if
onFulfilled
或onRejected
An exception was throwne
,promise2
Shall be based one
forreason
Be rejected. - if
onFulfilled
It’s not a function andpromise1
alreadyfulfilled
,promise2
Have to bepromise1
The value of thefulfilled
. - if
OnReject
It’s not a function andpromise1
alreadyrejected
,promise2
Must be the samereason
Be rejected.
MyPromise.prototype.then = function(onFulfilled, onRejected) {
let that = this;
let promise2;
// If the then argument is not function, then we need to ignore it
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(f) {};
onRejected = typeof onRejected === 'function' ? onRejected : function(r) {};
This is a pity pity. This is a pity pity. This is a pity pity.
// We have two arrays of on☆ darray
if(that.status === 'pending') {
return promise2 = new Promise(function(resolve, reject) {
that.onFullfilledArray.push((value) = > {
try {
let x = onFulfilled(that.value);
This is a pity pity. If it is, pass the resolve and reject in MyPromise to then.
// Return a Promise object and take its result directly as the result of promise2
if(x instanceof MyPromise) {
x.then(resolve, reject);
}
// If not, use its return value as the result of promise2
resolve(x);
} catch(error) { reject(error); }}); that.onRejectedArray.push((value) = > {
try {
let x = onRejected(that.value);
// Decide if onPromise.reject (); if it is, reject ();
// Return a Promise object and take its result directly as the result of promise2
if(x instanceof MyPromise) {
x.then(resolve, reject);
}
// If not, use its return value as the result of promise2
resolve(x);
} catch(error) { reject(error); }}); })}if(that.status === 'fulfilled') {
return promise2 = new MyPromise(function(resolve, reject) {
try {
let x = onFulfilled(that.value);
This is a pity pity. If it is, pass the resolve and reject in MyPromise to then.
// Return a Promise object and take its result directly as the result of promise2
if(x instanceof MyPromise) {
x.then(resolve, reject);
}
// If not, use its return value as the result of promise2
resolve(x);
} catch(error) { reject(error); }})}if(that.status === 'rejected') {
return new MyPromise(function(resolve, reject) {
try {
let x = onRejected(that.value);
// Decide if onPromise.reject (); if it is, reject ();
// Return a Promise object and take its result directly as the result of promise2
if(x instanceof MyPromise) {
x.then(resolve, reject);
}
// If not, use its return value as the result of promise2
resolve(x);
} catch(error) { reject(error); }}}})Copy the code
When calling then, it determines whether onFulfilled and onRejected are functions. If not, it returns an anonymous function and must return the values of the parameters to solve the problem of breaking through the Promise value in the chain-style call. Such as:
new MyPromise(resolve= >resolve(8))
.then()
.then()
.then(function foo(value) {
alert(value)
})
Copy the code
So let’s change this piece to look like this:
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(f) { return f};
onRejected = typeof onRejected === 'function' ? onRejected : function(r) {throw r};
Copy the code
However, each judgment requires rewriting the relationship between x and MyPromise, so we need to abstract this piece of code, which is called resolvePromise in the Promise/A+ specification.
function resolvePromise(promise, x, resolve, reject) {
let then,thenCalledOrThrow = false
// If the promise and x point to the same value, use TypeError to reject the promise as a cause.
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise! '))}// Decide if x is a Promise. If so, pass resolve and reject from MyPromise to THEN;
// Return a Promise object and take its result directly as the result of promise2
if((x ! = =null) && ((typeof x === 'object') | | (typeof x === 'function'))) {
try {
then = x.then
if (typeof then === 'function') { // typeof
//x.then(resolve, reject);
then.call(x, function rs(y) {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
return resolvePromise(promise, y, resolve, reject)
}, function rj(r) {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
return reject(r)
})
} else {
return resolve(x)
}
} catch(e) {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
return reject(e)
}
} else {
return resolve(x)
}
}
Copy the code
The then function is last modified to:
MyPromise.prototype.then = function(onFulfilled, onRejected) {
let that = this;
let promise2;
// If the then argument is not function, then we need to ignore it
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(f) { return f};
onRejected = typeof onRejected === 'function' ? onRejected : function(r) {throw r};
This is a pity pity. This is a pity pity. This is a pity pity.
// We have two arrays of on☆ darray
if(that.status === 'pending') {
return promise2 = new Promise(function(resolve, reject) {
that.onFullfilledArray.push((value) = > {
try {
let x = onFulfilled(value);
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
return reject(e)
}
});
that.onRejectedArray.push((value) = > {
try {
let x = onRejected(value);
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
returnreject(e) } }); })}if(that.status === 'fulfilled') {
return promise2 = new MyPromise(function(resolve, reject) {
try {
let x = onFulfilled(that.value);
// Handle multiple cases of then
resolvePromise(promise2, x, resolve, reject)
} catch(error) { reject(error); }})}if(that.status === 'rejected') {
return new MyPromise(function(resolve, reject) {
try {
let x = onRejected(that.value);
// Handle multiple cases of then
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
})
}
}
Copy the code
Test it out:
new MyPromise((resolve, reject) = > {
setTimeout((a)= > {
resolve(1);
}, 1000)
}).then((data) = > {
console.log(data);
return new MyPromise((res) = > {
setTimeout((a)= > {
res(2);
},1000)
})
}).then((res) = > {
console.log(res);
})
/ / 1
/ / 2
Copy the code
The last
Promise GITHUB address. References:
“Promise/A+ specification” “Promise3” “achieve A perfect compliance with Promise/A+ specification of the Promise” “Analysis Promise internal structure, step by step to achieve A complete, can pass all Test case of the Promise class”