1. The term
- 1.
promise
Is an object or function that has then methods - 2.
thenable
Is an object or function that has then methods - 3.
value
Is the value of the promise success state, and the value is of any type that conforms to the JS specification - 4.
exception
Is the value thrown by a throw exception - 5.
reason
Is the value of the promise failed state
2. Promise A + specification
2.1. Promise States
A promise must be in one of three states:
- 1. Pending
- This is a big pity. ∏ is my dream
- 3. Rejected
2.1.1. Pending
- The initialization state can be changed
- It can go to a success state or a failure state
2.1.2. Fulfilled
- The final state is immutable
- pending->resolve(value)->fulfilled
- You must have a value
2.1.3. Rejected
- The final state is immutable
- pending->reject(reason)->rejected
- You must have a reason value
2.1.4. Change of state
This is a big pity. “pending->reject-> Rejected” is a big pity. “Pending ->throw new Error(”)-> Failed” pending->throw new Error(”)-> Rejected
2.2. Then the method
A Promise must provide a THEN method that accesses the final result, either value or Reason
promise.then(onFulfilled,onRejected);
Copy the code
2.2.1. Parameter Requirements
- if
onFulfilled
Is not a function and must ignore value penetration. - if
onRejected
Is not a function and must ignore value penetration.
2.2.2. OnFulfilled
- When the state becomes depressing, ondepressing should be called with the parameter value
- OnFulfilled is not called until the state is fulfilled
- Ondepressing cannot be called many times
2.2.3. OnRejected
- When the status is Rejected, use onRejected and set the parameter to reason
- OnRejected is not called until the state is Rejected
- OnRejected cannot be called multiple times
2.2.4. OnFulfilled and onRejected should be performed asynchronously
This can be done through macro tasks or microtasks
- Macro task: setTimeout or setImmediate
- Microtasks: MutationObserver or process.nexttick
2.2.5. The then method can be called multiple times
- After the promise state becomes a big pity, all the ondepressing callbacks need to be executed in the order of THEN, that is, in the order of registration
- After the promise status changes to Rejected, all onRejected callbacks need to be executed in the order of THEN, that is, in the order of registration
2.2.6. The return value of then
The return value for then should be a new Promise
promise2 = promise1.then(onFulfilled, onRejected);
Copy the code
- OnFulfilled or onRejected execution result is x, call resolvePromise (promise2, x, resolve, reject)
- If onFulfilled or onRejected executes an exception, promise2 needs to reject the exception
- If the ondepressing is not a function, promise2 triggers the depressing with the value of promise1
- If onRejected is not a function, promise2 triggers the Rejected function with the reason of promise1
2.2.7. ResolvePromise
resolvePromise(promise2,x,resolve,reject);
Copy the code
-
If promise2 is the same as X, reject a type exception “TypeError or an infinite loop”
-
If x is a promise
- If X is in the pending state, the promise must continue to wait until x becomes a pity/Rejected
- If the X is fulfilled during the depressing state, the promise will be fulfilled with the same value
- If x is in the Rejected state, the Promise rejects for the same reason
-
If x is an object or function
-
let then=x.then
-
- If x. Chen fails, then promise rejects for the same reason
-
- If then is a function, then the call (x, resolvePromiseFn, rejectPromiseFn)
- ResolvePromiseFn into is y, perform resolvePromise (promise2, y, resolve, reject)
- Reject (r) with r as its input parameter.
- If both resolvePromiseFn and rejectPromiseFn are invoked, the first call takes precedence and subsequent calls are ignored
-
- If then executes an exception
- If resolvePromiseFn or rejectPromiseFn is already invoked, ignore it
- Reject Exception thrown
-
-
If then is a normal value, resolve(x) is straightforward;
3. The implementation
3.1. Define three states
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
Copy the code
3.2.class implements a Promise
- 1. New Promise is passing a constructor “executor”
- 2. Executor is executed by default and takes resolve and reject
- The resolve function, which handles wait state to success state and assigns value
- Reject (reject); reject (reject); reject (reject)
- 5. A reject exception is required when an error occurs during executor execution
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class Promise {
constructor(executor) {
// Initialization state
this.state = PENDING;
// Success is a value
this.value = null;
// Failure value
this.reason = null;
const resolve = (value) = > {
if (this.state === PENDING) { //pending->resolve->fulfilled
this.state = FULFILLED;
this.value = value; }}//pending->reject(reason)->rejected
const reject = (reason) = > {
if (this.state === PENDING) {
this.state = REJECTED;
this.reason = reason; }}try { //pending->throw new Error->rejected
executor(resolve, reject);
} catch(error) { reject(error); }}}Copy the code
3.3. Then the method
3.3.1. OnFulfilled onRejected
- OnFulfilled,onRejected
- Ondepressing is not a function, the value will pass down the “value penetration”.
- The onRejected is not a function the value passes down the value pass down
then(onFulfilled,onRejected){
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val= > val;
onRejected = typeof onRejected === 'function' ? onRejected : e= > {
throw e
}
}
Copy the code
The return value of 3.3.2. Then is a new PROMsie
If the return value of then is not a new promise, there is a problem with the violation that once success or failure is final, the subsequent THEN can’t be processed
then(onFulfilled,onRejected){
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val= > val;
onRejected = typeof onRejected === 'function' ? onRejected : e= > {
throw e
}
const promise2=new Promise((resolve,reject) = >{})
return promise2;
}
Copy the code
3.3.3. Handle different states
- This is a pity: the process of immediate success
- REJECTED: Indicates the processing when you fail immediately
then(onFulfilled,onRejected){
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val= > val;
onRejected = typeof onRejected === 'function' ? onRejected : e= > {
throw e
}
const promise2=new Promise((resolve,reject) = >{
switch (this.state) {
case FULFILLED:
onFulfilled(this.value);
break;
case REJECTED:
onRejected(this.reason);
break; }})return promise2;
}
Copy the code
3.3.4. Wait state processing
This is a pity or Rejected function. This is a pity or rejected function. This is a pity or rejected function. At this time, the state is essentially in the waiting state, so we need a monitoring mechanism of the state. When the state becomes a pity or Rejected, we will perform callback. For this, we adopt the publish and subscribe mode
- 1. Create a _led_callback_list to store successful callbacks
- 2. Create a _rejected_callback_list to store failed callbacks
- 3. Execute the subscription loop when pending->resolve-> depressing
- 4. Loop through pending-> Reject -> Rejected
const promise2=new Promise((resolve,reject) = >{
switch (this.state) {
case FULFILLED:
onFulfilled(this.value);
break;
case REJECTED:
onRejected(this.reason);
break;
case PENDING:
this._fulfilled_callback_list.push(
() = > {
onFulfilled(this.value);
})
this._rejected_callback_list.push(
() = > {
ronRejected(this.reason); })}})Copy the code
// this is a big pity
this._fulfilled_callback_list = [];
// Store the wait state onRejected
this._rejected_callback_list = [];
const resolve = (value) = > {
//may transition to either the fulfilled or
rejected state.
if (this.state === PENDING) { //
pending->resolve->fulfilled
this.value = value;
this.state = FULFILLED;
this._fulfilled_callback_list.forEach
(cb= >cb()); }}//pending->reject(reason)->rejected
const reject = (reason) = > {
//may transition to either the fulfilled or
rejected state.
if (this.state === PENDING) {
this.reason = reason;
this.state = REJECTED;
this._rejected_callback_list.forEach(cb= >cb()); }}Copy the code
3.3.5. Promise2
- This is a big pity, onRejected may be wrong, promise2 must refuse to perform
- OnFulfilled, onFulfilled return value, which may be of any type “Promise…” ResolvePromise is required to handle different cases
- 3. ResolvePromise (promise2, x, resolve, reject) the promise2 may not exist, the need to deal with “here” is realized by using setTimeout asynchronous
//A promise must provide a then method to access its current or eventual value or reason
then(onFulfilled, onRejected) {
//If onFulfilled is not a function, it must be ignored.
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val= > val;
//If onRejected is not a function, it must be ignored.
onRejected = typeof onRejected === 'function' ? onRejected : e= > {
throw e
}
const promise2 = new Promise((resolve, reject) = > {
let realOnFulfilled = () = > {
setTimeout(() = > { //onFulfilled or onRejected must not be called until the execution
context stack contains only platform code
try {
//If either onFulfilled or onRejected returns a value x, run the Promise
Resolution Procedure [[Resolve]](promise2, x)
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch(error) { reject(error); }},0);
}
let realOnRejected = () = > {
setTimeout(() = > { //onFulfilled or onRejected must not be called until the execution
context stack contains only platform code
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch(error) { reject(error); }},0);
}
switch (this.state) {
case FULFILLED:
realOnFulfilled();
break;
case REJECTED:
realOnRejected();
break;
case PENDING:
this._fulfilled_callback_list.push(() = > {
realOnFulfilled();
})
this._rejected_callback_list.push(() = > {
realOnRejected();
})
break; }});return promise2;
}
Copy the code
3.3.6. ResolvePromise
- 1. X is the same as promise2 in an infinite loop
- 2. X is a Promise. “Here is my Promise”
- 3. X is an object or function
- 3.1.x has the then method, indicating that it is a thenable
- 3.2. X without THEN indicates a common object or NULL
- 4. X is a common value
function resolvePromise(promise2, x, resolve, reject) {
//If promise and x refer to the same object, reject promise with a TypeError as the reason.
if (promise2 === x) {
reject(new TypeError('Dead loop'))}else if (x instanceof Promise) { //If x is a promise, adopt its state
x.then(y= > {
resolvePromise(promise2, y, resolve, reject);
}, reject);
} else if ((typeof x === 'object'&& x ! = =null) | | (typeof x === 'function')) { //Otherwise, if x is an object or function
let called = false;
try {
//Let then be x.then
let then = x.then;
if (typeof then === 'function') { //If then is a function
//call it with x as this
then.call(x, y= > { //first argument resolvePromise
if (called) return;
called = true;
//If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)
resolvePromise(promise2, y, resolve, reject);
}, r= > { //second argument rejectPromise
if (called) return;
called = true;
reject(r); //If/when rejectPromise is called with a reason r, reject promise with r})}else { //If then is not a function, fulfill promise with x.resolve(x); }}catch (error) { //If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason
if (called) return;
called = true; reject(error); }}else { //If x is not an object or function, fulfill promise with xresolve(x); }}Copy the code
3.3.7. Test the promise of implementation
Now that we’ve implemented A Promise according to the Promise A+ specification, let’s test the Promise we wrote
npm install promises-aplus-tests -g
Copy the code
Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve, reject) = > {
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
Copy the code
promises-aplus-tests promise.js
Copy the code
3.4. The catch
catch (onRejected) {
return this.then(null, onRejected);
}
Copy the code
3.5. Promise. Resolve to achieve
- 1. The resolve method is static
- 2. The resolve argument can be of any type
- 3. The result of resolve is a promise
static resolve(value) {
if (value instanceof Promise) return value;
return new Promise(resolve= >{ resolve(value); })}Copy the code
3.6. Promise. Reject
- 1. Reject is a static method
- 2. Reject can be any type
- 3. Reject returns a promise
static reject(reason) {
return new Promise((_, reject) = >{ reject(reason); })}Copy the code
3.7. Promise. All implementations
The problem of asynchronous concurrency can be solved and the results returned are stored in the order in which they were called. Success only after all success otherwise failure logic is executed
- 1. The all method is static
- 2. The parameter of all can be any type of array
- 3. All returns a promise
static all(promiseList) {
return new Promise((resolve, reject) = > {
let len = promiseList.length,
timers = 0;
result = [];
const resolveResult = (value, index) = > {
result[index] = value;
if(++timers === len) { resolve(result); }}for (let i = 0; i < len; i++) {
const value = promiseList[i];
if (isPromise(value)) {
value.then(x= > {
resolveResult(x, i);
}, reject)
} else{ resolveResult(value, i); }}})}Copy the code
3.8. Promise. Race
Race problem handling, whether successful or unsuccessful, returns the first value returned
- 1. The RACE method is static
- 2. Race parameters can be any type of array
- 3. The return result of race is a promise
static race(promiseList) {
return new Promise((resolve, reject) = > {
let len = promiseList.length;
if (len === 0) resolve();
else {
for (let i = 0; i < len; i++) {
Promise.resolve(promiseList[i]).then
(value= > {
resolve(value)
}, reason= >{ reject(reason); })}}})}Copy the code
3.9. The finally realized
- 1. Finally callback has no arguments
- 2 finally returns a promise
- 3. Finally executes regardless of success or failure
- 4. Finally successful values are not used as values for the next THEN
- 5. Finally, a failed value will end the chain, and its failed value will be used as the value of the next then
finally(callback) { // Callback has no arguments
return this.then(data= > { // Whether you succeed or failCallback will execute// Callback executes, possibly a promise that needs to wait until it finishes executing
成
// Success requires passing the then value, not the callback value
return Promise.resolve(callback()).then(_= > data);
}, error= > {
Resolve needs to be called because reject does not wait in thenThrow out the error valuereturn Promise.resolve(callback()).then(_= > {
throw error
});
});
}
Copy the code