Promise series
- Promise – Three questions of the soul
- Promise – Handwritten source code
- Promise – Fully functional
In the previous article, we implemented A simple Promise under the Promise A+ protocol, and the test passed. This time, we’ll complete some of the commonly used apis for Promise. Firstly, we divide THE API into static methods and prototype methods. The static methods include :resolve, Reject, All, Race, allSettled, and prototype methods: Catch, finally.
A static method
Promise.resolve(value)
The method stipulates:
- Method returns a parsed Promise object for a given value.
- If value is a non-Promise object, it is returned as an argument
- If value is a Thenable Promise object, the final state of the flattened Promise is returned
static resolve(value) {
return new Promise((resolve, reject) = > {
// If a non-Promise object is returned
// In the case of a Promise object, we need to do something in the constructor's resolve method, passing the Promise object into then and passing our resolve,reject as the two callbacks to the THEN if the subsequent then completes the callback and calls resolve/rej Ect also informs the outer Promise whether it succeeds or fails
resolve(value)
})
}
Copy the code
Promise.reject(reason)
The method returns a Promise object with a reason for the rejection
static reject(reason) {
return new Promise((resolve,reject) = > {
reject(reason)
})
}
Copy the code
Promise.all(promise)
The method stipulates:
- Accepts a traversable array, which can be a Promise object or a non-Promise object
- Return a Promise instance
- The Promise’s resolve is at the end of all successful callbacks, passing the non-deconstructed/deconstructed values as an array of values in order, and the array as the value of resolve
- When Promise deconstructs a failure, it invokes the failure directly, Reject
static all(promises) {
// Determine if a promise can be iterated, and throw an exception if not.return Promise((resolve,reject) = > {
// hold all callback or unstructured values, and resolve
const results = []
let times = 0
function resloveData(data, index) {
Return values in the same order as those passed in
results[index] = data
times++
if(times === promises.length) {
resolve(results)
}
}
for(let index = 0; index < promises.length; index++) {
const _p = promises[index]
if(isPromise(_p)) {
// If it is an instance of Promise, then
_p.then(value = {
resloveData(value,index)
}, reason= > {
// If it fails, change the Promise state to failed
reject(reason)
})
} else {
// If it's not a Promise instance, add it to the returned array
resloveData(_p,index)
}
}
})
}
Copy the code
Promise.race(promise)
Method definition:
- Accepts a traversable array, which can be a Promise object or a non-Promise object
- Return a Promise instance
- The Promise’s resolve is at the end of the first callback, returning the first callback value as a success or failure value
- The race, as long as a value is taken, whether it is a win or a loss, or a non-deconstructed value, is returned directly
static race(promises) {
return new Promise((resolve, reject) = > {
for (let index = 0; index < promises.length; index++) {
const _p = promises[index]
if (isPromise(_p)) {
_p.then(
(value) = > {
resolve(value)
},
(reason) = > {
reject(reason)
}
)
} else {
resolve(_p)
}
}
})
}
Copy the code
Promise.allSettled(promise)
Method definition:
- Accepts a traversable array, which can be a Promise object or a non-Promise object
- Return a Promise instance
- The Promise’s resolve is passed in as the value of resolve at the end of all callbacks, with the undeconstructed/deconstructed values and loaded as a set of numbers
- Both success and failure will return in success
static allSettled(promises) {
return new Promise((resolve, reject) = > {
const results = []
let times = 0
function setData(index, value, reason, mStatus) {
results[index] = {
status: mStatus,
value: value,
reason: reason,
}
times++
if (times === promises.length) {
resolve(results)
}
}
for (let index = 0; index < promises.length; index++) {
const _p = promises[index]
if (isPromise(_p)) {
_p.then(
(data) = > {
setData(index, data, undefined.'fulfilled')},(reason) = > {
setData(index, undefined, reason, 'rejected')})}else {
setData(index, _p, undefined.'fulfilled'}}})}}Copy the code
Prototype chain method
The prototype we use most often is catch,finally, but note that the catch and finally here are different from our normal trycatch.
Promise.prototype.catch(err=>{})
Catch can be seen as then without onledcallback
catch(error) {
return this.then(null,error)
}
Copy the code
Promise.prototype.finally(() => {})
Some special points about finally
- The callback method has no value
- The callback method must be executed
- Return a Promise to continue the chain call
finally(callback) {
return this.then(data= > {
return Promise.resolve(callback()).then(() = > data)
}, reason= > {
return Promise.resolve(callback()).then(() = > {
throw reason
})
})
}
Copy the code
The complete code
// Promise has three states: wait, success, and reject
const STATUS = {
PENDING: 'PENDING'.FULFILLED: 'FULFILLED'.REJECTED: 'REJECTED',}class Promise {
constructor(executor) {
// Promise defaults to Pending
this.status = STATUS.PENDING
this.value = undefined // Store success values for subsequent chain calls
this.reason = undefined // Store the cause of the failure to facilitate subsequent chain calls
// If the state does not change and then is called, we need to store the successful/failed methods in then and wait until the state changes
this.onFulfilledCallbacks = [] // Then's set of successful methods
this.onRejectedCallbacks = [] // Set of failed methods for then
// The user calls the resolve function to change the state of the current instance when executing the function correctly. The resolve function supports passing in an argument as a success value
const resolve = (value) = > {
// Determine if the value passed in is a Promise. If it is still a Promise, return the call to THEN directly, passing the success and failure callbacks of resolve/reject to then. If the Promise succeeds/fails, modify the current state of the Promise with the callback argument
if (value instanceof Promise) {
return value.then(resolve, reject)
}
// The promise state can be changed only in the wait state
if (this.status === STATUS.PENDING) {
// Change the instance status to successful state
this.status = STATUS.FULFILLED
// Accept the success value from the user
this.value = value
// After the state changes, the successful method set call then is initiated to implement asynchronous operation
this.onFulfilledCallbacks.forEach((fn) = > fn())
}
}
Reject is called by the user to modify the state of the current instance when an error occurs or an exception is thrown. Reject supports passing in a parameter as the cause of failure or rejection
const reject = (reason) = > {
// Promise can only be modified in the wait state
if (this.status === STATUS.PENDING) {
// Change the state of the instance to failed
this.status = STATUS.REJECTED
// Accept the failure cause sent by the user
this.reason = reason
// After the state changes, the successful method set call then is initiated to implement asynchronous operation
this.onRejectedCallbacks.forEach((fn) = > fn())
}
}
try {
// New Promise() passes in a function that executes by default and takes two executable method arguments
executor(resolve, reject)
} catch (error) {
// If the default function fails, the state is set to reject
reject(error)
}
}
/** * 1. Each Promise prototype has a THEN method on it that provides two executable method arguments *@param {*} Ondepressing successfully performs the function *@param {*} 2. The then method must return a promise. We do this by new a new promise *@returns New Promise() * 3. Then if the current successful/failed method is called, if an exception is thrown, then the next failed method will be used * + return the success/failure value x * + if x is an object * * + x is a Promise that obtains the THEN of X and continues to invoke the THEN until it succeeds in obtaining the normal object or base data type * + Successful and failed methods invoke the Promise in the promise2 THEN Resolve /reject methods * + The success or failure of the next THEN is determined by the success or failure of X * + If x is the base data type, the success method of the next THEN will be called (success or failure of the then method will be called) * If the Promise returned by the callback fails, then the next call will be resolved. If the Promise returned by the callback fails, then the next call will be resolved
then(onFulfilled, onRejected) {
// If onFulfilled/onRejected cannot obtain the current success/failure value to the next THEN
// New promise((resolve,reject) =>{resolve(1)}).then().then().then(of=> {console.log(of)}) needs to be able to pass in the value of
onFulfilled =
typeof onFulfilled === 'function' ? onFulfilled : (data) = > data
onRejected =
typeof onRejected === 'function'
? onRejected
: (e) = > {
throw e
}
// Create a new promise2 and return it at the end of the method, then reaches the chain call, continuing then is actually calling the new promise instance
const promise2 = new Promise((resolve, reject) = > {
// When we call the then method, we call different pass-the-argument methods by determining the current Promise state
// When the promise state is Fulfilled, the onFulfilled method is called and the success value is passed in as the parameter
if (this.status === STATUS.FULFILLED) {
// We need to handle promise2 and x, but promise2 is assigned after new, so we need to use an asynchronous macro/micro to get the promise
setTimeout(() = > {
try {
const x = onFulfilled(this.value)
// We use an external method to unify the processing here
resolvePromise(x, promise2, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)}// When the promise status is REJECTED, call the onRejected method and pass the failure reason as an argument
if (this.status === STATUS.REJECTED) {
setTimeout(() = > {
try {
const x = onRejected(this.reason)
// We use an external method to unify the processing here
resolvePromise(x, promise2, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)}// When the state of a promise is PENDING, it is an asynchronous operation
// Publish the resolve and reject methods in publish-subscribe mode and invoke them when subsequent state changes are complete
if (this.status === STATUS.PENDING) {
// Use decorator pattern/slicing to wrap the success/failure methods so that we can do other operations and get value/ Reason after state changes
this.onFulfilledCallbacks.push(() = > {
setTimeout(() = > {
try {
const x = onFulfilled(this.value)
// We use an external method to unify the processing here
resolvePromise(x, promise2, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)})this.onRejectedCallbacks.push(() = > {
setTimeout(() = > {
try {
const x = onRejected(this.reason)
// We use an external method to unify the processing here
resolvePromise(x, promise2, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)})}})return promise2
}
/** * promise.prototype. Catch * call reject *@param {*} err
* @returns* /
catch(err) {
return this.then(null, err)
}
/** * First understand that the finally function is not a try/catch function and will be executed anyway * Can return a Promise */
finally(callback) {
return this.then(
(data) = > {
The promise.resolve () method is called internally, and if the method is Promise, wait for it to complete
return Promise.resolve(callback()).then(() = > data)
},
(err) = > {
return Promise.resolve(callback()).then(() = > {
throw err
})
}
)
}
/********* Static method *******/
/** * promise.resolve () implements * + if resolve is passed to a primitive value, the value will be passed to a Promise, and the Promise will support resolve(new) Promise()), i.e. * constructor(executor){*... * const resovle = function(value) { * ... * if (value instanceof Promise) { * return value.then(resolve, reject) * } * ... *} *... * * *}@param {*} value
* @returns* /
static resolve(value) {
return new Promise((resolve, reject) = > {
resolve(value)
})
}
static reject(reason) {
return new Promise((resolve, reject) = > {
reject(reason)
})
}
/** * Promise. All returns a Promise */
static all(promises) {
return new Promise((resolve, reject) = > {
const result = []
let times = 0
function promiseData(index, val) {
result[index] = val
if (++times === promises.length) {
resolve(result)
}
}
for (let index = 0; index < promises.length; index++) {
const _p = promises[index]
// Check if it is Promise
if (isPromise(_p)) {
_p.then((data) = > {
promiseData(index, data)
}, reject)
} else {
promiseData(index, _p)
}
}
})
}
static race(promises) {
return new Promise((resolve, reject) = > {
for (let index = 0; index < promises.length; index++) {
const _p = promises[index]
if (isPromise(_p)) {
_p.then(
(value) = > {
resolve(value)
},
(reason) = > {
reject(reason)
}
)
} else {
resolve(_p)
}
}
})
}
static allSettled(promises) {
return new Promise((resolve, reject) = > {
const results = []
let times = 0
function setData(index, value, reason, mStatus) {
results[index] = {
status: mStatus,
value: value,
reason: reason,
}
times++
if (times === promises.length) {
resolve(results)
}
}
for (let index = 0; index < promises.length; index++) {
const _p = promises[index]
if (isPromise(_p)) {
_p.then(
(data) = > {
setData(index, data, undefined.'fulfilled')},(reason) = > {
setData(index, undefined, reason, 'rejected')})}else {
setData(index, _p, undefined.'fulfilled'}}})}}function isPromise(value) {
return value && typeof value.then === 'function'
}
// Handle subsequent state changes to the Promise(promise2) returned in then
function resolvePromise(x, promise2, resolve, reject) {
// If promise2 is passed in by itself
if (x === promise2) {
return reject(new TypeError('Repeat call'))}// Determine if the passed x is an object or method
if ((typeof x === 'object'&& x ! = =null) | |typeof x === 'function') {
// In order to be compatible with other promises, add an anti-repetition feature to prevent other promises from being called repeatedly
let called
Reject (x) if an exception is thrown
try {
// Get the then attribute of x
const then = x.then
// If then is function, we default x to Promise
if (typeof then === 'function') {
// Call the then method of x, and call the corresponding success and failure if x succeeds/fails internally
then.call(
x,
(y) = > {
if (called) return
called = true
// If y is still a Promise, then needs to be resolved again to know that it is not a Promise
// resolve(y)
resolvePromise(y, promise2, resolve, reject)
},
(r) = > {
if (called) return
called = true
reject(r)
}
)
} else {
// If then is not a function, we default x to a non-promise object. Resolve (x)
resolve(x)
}
} catch (error) {
if (called) return
called = true
reject(error)
}
} else {
Resolve (x) if the value passed is a primitive value type
resolve(x)
}
}
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) = > {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise
Copy the code