This article mainly through the example of the use of promise, to realize a promise.
State initialization
Promise usage
let p = new Promise((res, rej) = > {
// res('success')
rej('err')})console.log(p1);
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: "success"
Copy the code
Promise initializes the implementation
First, make sure that a promise can be implemented with a class. When a promise is initialized, it needs to know the state of the promise and the return value of the promise.
In a promise, there are two properties, PromiseState and PromiseResult, which store the state and result of the promise respectively.
When a user passes a new Promise, they pass in a callback function that uses resolve and Rejected.
- When you call resolve, change the state to depressing and assign a value to the result.
- When you call REJECT, change the state to Rejected and assign a value to the result.
Initialization is as follows:
class MyPromise {
constructor(cb) {
this['[[PromiseState]]'] = 'pending'
this['[[PromiseResult]]'] = undefined
this.onResolved = [] The two collections are not currently in use
this.onRejected = [] // Store the failed callback collection
cb(this.#resolve.bind(this), this.#reject.bind(this)) // We need to bind this, otherwise we can't get it
}
#resolve(val) {
this['[[PromiseState]]'] = 'fulfilled'
this['[[PromiseResult]]'] = val
}
#reject(val) {
this['[[PromiseState]]'] = 'rejected'
this['[[PromiseResult]]'] = val
}
}
Copy the code
If the cb call does not bind this to the promise internal private property, an error will be reported:The output of this is undefined, so we can’t get this, so we need to bind this when cb calls.
A static method
resolve
Promise.resolve() returns a resolved Promise object.
Method of use
console.log(Promise.resolve('success'));
// Promise{
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: "success"
// }
Copy the code
implementation
Use the resolve static method directly and output the same result as new Promise((res, rej) => {res(‘success’)}). Therefore, when calling the resolve method, you need to instantiate a Promise class that calls res internally.
static resolve(val) {
return new MyPromise((res, rej) = > {
res(val)
})
}
Copy the code
reject
Promise.reject() returns a rejected Promise object.
Method of use
console.log(Promise.reject('err'));
// Promise{
// [[PromiseState]]: "rejected"
// [[PromiseResult]]: "err"
// }
Copy the code
implementation
Using the resolve static method directly, the output is the same as new Promise((res, rej) => {rej(‘err’)}).
static reject(val) {
return new MyPromise((res, rej) = > {
rej(val)
})
}
Copy the code
allSettled
Promise.allsettled () accepts an array of objects composed of the results of a fulfilled Promise (state: fulfilled or rejected).
AllSettled usage
let p2 = new Promise((res, rej) = > {
setTimeout(() = > {
res("p1");
}, 2000);
})
let p3 = new Promise((res, rej) = > {
setTimeout(() = > {
rej("p2 err");
}, 1000);
})
Promise.allSettled([p2, p3]).then(res= > {
console.log(res); // [{status: "fulfilled", value: "p1"},{status: "rejected", reason: "p2 err"}]
})
Copy the code
Implementation method
AllSettled receives an array of objects and outputs an array regardless of state. When the array loop calls THEN, you need to consider the return results of two states, and in each state, format the data returned from that item. For example, {status: “depressing “, value: “p1”}, the two attributes in success are status and value respectively.
At implementation time, you need to set a variable that stores the length of the new result, num. If num is equal to the length of the received array lists, it indicates that the uniform format has been changed and the next callback needs to be performed.
static allSettled(lists) {
let resArr = new Array(lists.length)
let num = 0
return new MyPromise((resolve) = > {
lists.forEach((item, key) = > {
let obj = {}
item.then(
(res) = > {
obj['status'] = 'fulfilled'
obj['value'] = res
resArr[key] = obj
num++
if (num >= lists.length) {
resolve(resArr)
}
},
(err) = > {
obj['status'] = 'rejected'
obj['reason'] = err
resArr[key] = obj
num++
if (num >= lists.length) {
resolve(resArr)
}
},
)
})
})
}
Copy the code
all
Promise.all waits for everything to complete, and if there is an error in the array in all, an exception is executed.
All usage
let p2 = new Promise((res, rej) = > {
setTimeout(() = > {
res("p1");
}, 2000);
})
let p3 = new Promise((res, rej) = > {
setTimeout(() = > {
res("p2");
}, 1000);
})
Promise.all([p2, p3])
.then(res= > {
console.log(res);
})
// ["p1", "p2"]
Copy the code
All implementation
Like allSettled, you only need to consider the success case, but still need to determine if everything is executed.
static all(lists) {
let newArr = new Array(lists.length)
let num = 0
return new MyPromise((resolve) = > {
lists.forEach((item, key) = > {
item.then((res) = > {
newArr[key] = res
num++
if (num >= lists.length) {
resolve(newArr)
}
})
})
})
}
Copy the code
race
Promise.race() executes whoever has a short execution time, regardless of state.
Race usage
let p1 = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('success')},1000)})let p2 = new Promise((resolve, reject) = > {
setTimeout(() = > {
reject('failed')},500)})Promise.race([p1, p2]).then((result) = > {
console.log(result)
}).catch((error) = > {
console.log(error) // 打开的是 'failed'
})
Copy the code
Race to achieve
When traversing lists, call then directly.
static race(lists) {
return new MyPromise((resolve, reject) = > {
lists.forEach((item) = > {
item.then(
(res) = > {
resolve(res)
},
(err) = > {
reject(err)
},
)
})
})
}
Copy the code
any
Promise.any() returns the first of the successful states.
Any usage
let p1 = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('success')},1000)})let p2 = new Promise((resolve, reject) = > {
setTimeout(() = > {
reject('failed')},500)})Promise.any([p1, p2]).then((result) = > {
console.log(result)
}).catch((error) = > {
console.log(error)
})
// success
Copy the code
Any implementation
Performs the first callback for the successful state. If none of the iterable promises succeeds (i.e. all Promises fail/reject), return an instance of the failed promise and AggregateError type.
static any(lists) {
return new MyPromise((resolve, reject) = > {
let errs = []
let len = lists.length
if (len === 0) reject(new AggregateError('All promises were rejected'))
lists.forEach(item= > {
item.then(res= > {
resolve(res)
}, err= > {
len--
errs.push(err)
if (len === 0) reject(new AggregateError(errs))
})
})
})
}
Copy the code
Prototype method
Then prototype method
Promise.prototype.then() accepts up to two callbacks: those in the case of success and failure.
Basic then
Basic then usage
let p = new Promise((res, rej) = > {
res('success');
})
p.then(res= > {
console.log(res); // success
}, err= > {
console.log(err);
})
Copy the code
Implementation of basic THEN
Based on the basic usage of THEN in the previous paragraph, the RES or REJ executed in New Promise corresponds to the correct callback or error thrown, respectively. In then you can determine the state of the promise to perform different callbacks.
then(onFullfill, onReject) {
if (this['[[PromiseState]]'= = ='fulfilled') {
onFullfill(this['[[PromiseResult]]'])}if (this['[[PromiseState]]'= = ='rejected') {
onReject(this['[[PromiseResult]]'])}}Copy the code
This is a big pity, which means success. If it is rejected, it indicates failure. When multiple THEN’s are invoked, the release callback is invoked based on the call in the Promise, and the corresponding callback from multiple THEN’s is also fired.
let p = new MyPromise((res, rej) = > {
res('success')}); p.then(res= > {
console.log(res);
}, err= > {
console.log('err', err);
})
p.then((res) = > {
console.log(res, '2');
}, (rej) = > {
console.log('err2');
})
// success
// success 2
Copy the code
Chain calls to THEN
The chained use of then
let p1 = new Promise((res, rej) = > {
res('success');
})
p1.then(res= > {
return new Promise((res, rej) = > {
res('success2')})},err= > {
console.log(err);
}).then(res= > {
console.log('2', res); // 2 success2
})
Copy the code
Chain implementation of THEN
A chained implementation is the equivalent of returning a promise. Based on the state of the previous promise, the corresponding state callback is performed in then, and so on. If a promise is returned from the previous THEN, the state in that THEN is ‘pending’.
#resolve(val) {
if (this['[[PromiseState]]']! = ='pending') return
setTimeout(() = > {
this['[[PromiseState]]'] = 'fulfilled'
this['[[PromiseResult]]'] = val
// this.onResolvedQueue.forEach((fn) => fn && fn(val))
let cb;
while(cb = this.onResolvedQueue.shift()) {
cb(val)
}
})
}
#reject(err) {
if (this['[[PromiseState]]']! = ='pending') return
setTimeout(() = > {
this['[[PromiseState]]'] = 'rejected'
this['[[PromiseResult]]'] = err
// this.onRejectedQueue.forEach((fn) => fn && fn(err))
let cb;
while(cb = this.onRejectedQueue.shift()) {
cb(err)
}
})
}
then(onFullfill, onReject) {
// Returns a new promise
return new MyPromise((resolve, reject) = > {
// Successfully executed function
let onFullfillFn = (val) = > {
let res = onFullfill && onFullfill(val)
// Returns a new promise to perform the next callback
res instanceof MyPromise ? res.then(resolve) : resolve(res)
}
// Failed to execute the function
let rejectFn = (err) = > {
onReject && onReject(err)
reject(err)
}
this.onResolvedQueue.push(onFullfillFn)
this.onRejectedQueue.push(rejectFn)
})
}
Copy the code
A maximum of two callback functions are accepted in then. In a successful callback, the incoming callback may be directly logical processing, or a new Promise object may need to be returned, so you need to determine the return type. If a failed function is passed in, the user failed callback is executed directly.
Catch prototype method
Promise.prototype. Catch () Catch the Rejected condition and reject it.
The usage of catch
let p = new Promise((res, rej) = > {
rej('err')
})
p.catch((reason) = > {
console.log(reason) // err
})
Copy the code
Catch implementation
Failed callback to execute THEN directly.
catch(cb) {
this.then(undefined, cb)
}
Copy the code
Finally prototype method
Promise. Prototype. Finally () being fulfiled or rejected, will perform the finally in the callback function.
The finally usage
let p = new Promise((res, rej) = > {
res('success')
})
p.then((reason) = > {
console.log(reason)
}).finally(() = > {
console.log('finally');
})
// success finally
Copy the code
The finally realize
finally(cb) {
return this.then(cb, cb)
}
Copy the code