Promise Principle and Imitation Promise(Asynchronous II)
Basic features of Promise
Refer back to the last asynchronous topic notes for details
Note portal
We can perform a mock write practice based on the basic Promise functionality
-
Three states
- pending
- fulfilled
- rejected
-
Then methods for Promise objects
-
Two arguments to then
-
Three return values for then
Three states
We can start by creating a MyPromise class that takes a function with arguments reslove and Reject. Its default status is pending and value is undefined.
Creating an instance of MyPromise on the page gives this effect, and the pending effect is implemented
class Mypromise {
constructor(handle) {
this.status = 'pending';
this.value = undefined}}//result
Mypromise
status: "pending"
value: undefined
__proto__: Object
Copy the code
This is a pity,rejected observe a promise object. It takes a function with arguments reslove and reject. First it executes the function passed in, then reslove() and reject(). So it’s using a higher-order function. Executing reslove() and reject() changes its state and value.
Note that executing reslove() and Reject () on MyPromise changes the state and value of this to MyPromise, but executing this externally to Reslove () and Reject () is not fixed, and this instance points to Windows. There is no this.status or this.value in it. So internally we bind reslove() with reject()
//Mypromise.js
class Mypromise {
constructor(handle) {
this.status = 'pending';
this.value = undefined;
handle(this._resolve.bind(this),this._reject.bind(this))
}
_resolve (val){
this.status = 'fulfilled';
this.value = val;
}
_reject (val){
this.status = 'rejected';
this.value = val; }}// myPromise.html
<script>
const mPro = new Mypromise((res,rej) = >{
// res('res... ');
rej('rej... ')});console.log(mPro)
</script>
//result
Mypromise
status: "fulfilled"
value: "res..."
__proto__: Object
Mypromise
status: "rejected"
value: "rej..."
__proto__: Object
Copy the code
The then method of the Promise object, and the two parameters of the then
Then is the Promise method, so we can write the THEN method directly inside the class. But there was a problem. The onResolved onRejected method will be implemented in the THEN method.
Let’s look at the code first, and execute when the state changes. There is no problem with calling resolve and reject to change the state and synchronize the task
//Mypromise.js / then()
if (this.status === "fulfilled") {
onResolved && onResolved(this.value);
} else if (this.status === 'rejected') {
onRejected && onRejected(this.value);
}
console.log(this.status );
// myPromise.html
let p = new KPromise((resolve, reject) = > {
// resolve()
//setTimeout(() => {
/ / call onResolved
resolve("resolveValue") // Set the state
/ /}, 3000)
// reject("rejectValue");
})
p.then(res= > {
console.log("onResolved", res);
}, err= > {
console.log("onRejected", err);
})
Copy the code
But if we were to delay execution at the time of the call. You’ll notice that then is synchronous code, changing state to asynchronous code, and so on. There is a problem with executing the code inside then first, changing the state. So it’s always pending, so it can’t execute onResolved, onRejected.
let p = new KPromise((resolve, reject) = > {
// resolve()
setTimeout(() = > {
/ / call onResolved
resolve("resolveValue") // Set the state
}, 3000)
// reject("rejectValue");
})
Copy the code
OnResolved and onRejected are not implemented in then. It’s related to resolve and reject; So we can save this function in the then method, but do not execute it until the status changes.
So we can take advantage of asynchronous and synchronous execution of different order changes
//myPromise.html
const mPro = new Mypromise((res,rej) = >{
setTimeout(() = >{
res('res... ');
},1000)
//rej('rej... ');
});
mPro.then((res) = >{
console.log('onres... ');
},(err) = >{
console.log('err... ');
})
console.log(mPro);
//Mypromise.js
_resolve (val){
this.status = 'fulfilled';
this.value = val;
setTimeout(() = >{
this.onResolved (val);
})
}
_reject (val){
this.status = 'rejected';
this.value = val;
setTimeout(() = >{
this.onRejected (val); })}then(onResolved,onRejected){
this.onResolved = onResolved;
this.onRejected = onRejected
}
Copy the code
Then brings up two small problems
-
Multiple THEN call problem (note not chained call) queues;
When multiple then calls are made, subsequent functions override previous functions. But native promises are always implemented. The idea is that it’s not a simple function assignment save, it’s a queue save.
//mypromise.js _resolve (val){ this.status = 'fulfilled'; this.value = val; const run = () = > { this.onResolvedQueue.forEach(cb= >{ cb(val); })}setTimeout(run); } //then then(onResolved,onRejected){ this.onResolvedQueue.push(onResolved); this.onRejectedQueue.push(onRejected); } // html mPro.then((res) = >{ console.log(111) }) mPro.then((res) = >{ console.log(222)})Copy the code
-
KPromise execution order problem; (macro task, micro task related);
Native promises are microtasks, and myPromises, which encapsulate themselves, are macro tasks, so some tasks have problems executing.
* * macros to meet a new macro task in the task execution, will make the new acer task in task at the end of the queue, but a macro after completion of task execution, executing the task queue, in executing the task queue, meet new task, it won’t be on macro task at the end of the queue, but in a macro current tasks executed after the completion of the task queue. * * so new macros The new microtask will be executed before the new macro task
Micro tasks:
- Promise
- mutationObserver
- process.nextTick
Macro task:
- settimeout
- setInterval
So we’re going to turn our rewritten KPromise into a microtask, using a mutationObserver that listens for node changes.
A mutationObserver callback is triggered when the document.body attribute changes
setTimeout(() = > {
console.log("settimeout");
});
let callback = function(){
console.log("The micromission has been executed.");
}
let ob = new MutationObserver(callback)
ob.observe(document.body,{
attributes:true
})
document.body.setAttribute("123".Math.random());
Copy the code
rewrite
_resolve (val){
this.status = 'fulfilled';
this.value = val;
const run = () = > {
this.onResolvedQueue.forEach(cb= >{ cb(val); })}// setTimeout(run);
let ob = new MutationObserver(run);
ob.observe(document.body,{
attributes:true
})
document.body.setAttribute("attr".Math.random());
}
Copy the code
Three return values and chain calls for THEN (difficult)
The chain call returns an instance object
Three return values for THEN (difficult)
The then method must return a new Promise object at the end of execution
Key points (Difficult points)
Returning a Promise object is called and executed immediately, and executing either resolve or Reject directly results in subsequent THEN calls as well
We need to wrap the old handler and rejectedHandler, place them and the new Promise object’s resolve and Reject methods in the new function, and add the new function to the original task queue to call
In short: Add the resolve and Reject of the newly returned Promise object to a task queue, along with the ledHandler and rejectedHandler executed in then, This allows you to use the original THEN execution before executing the THEN in the new Promise
This is the default, but the then method is more complex
This is a big pity. When a Promise is fulfilled or fails, the return function will be called asynchronously (scheduled by the current thread loop). The specific return value is returned according to the following rules:
- If the callback function in then returns no value, the Promise returned by then becomes the accepted state, and the accepted state callback is undefined.
- If the callback function in THEN returns a value, the Promise returned by then becomes the accepted state, and the returned value is used as the parameter value of the callback function that accepts the state.
- If the callback in THEN throws an error, the Promise returned by then becomes the rejection state, and the thrown error is taken as the argument value of the rejection state callback.
- If the callback function in then returns a Promise that is already an accepted state, then the Promise returned by then becomes the accepted state, and the accept-state callback of that Promise is used as the accept-state callback of the returned Promise.
- If the callback function in then returns a Promise that is already a rejection state, then the Promise returned by then becomes a rejection state, and the value of the Promise’s rejection state callback is used as the value of the returned Promise’s rejection state callback.
- If the callback function in THEN returns a Promise that is pending, then the state that then returns a Promise is also pending and its final state is the same as that of the Promise; At the same time, the callback that it calls when it goes to its final state is the same as the callback that the Promise calls when it goes to its final state.
then(onResolved,onRejected){
// this.onResolvedQueue.push(onResolved);
// this.onRejectedQueue.push(onRejected);
return new Mypromise((resolve,reject) = >{
// Execute directly; Wrong;
// let res = onResolved && onResolved();
// // object;
// if(res instanceof Mypromise){
// return res;
// }
// // Ordinary value;
// resolve(res);
// push not to execute; Separate execution and logic
this.onResolvedQueue.push(val= >{
let res = onResolved && onResolved(val);
if(res instanceof Mypromise){
// Return Mypromise object
// return res.then(res=>{
// resolve(res)
// });
// console.log(resolve)
return res.then(resolve);
}
resolve(res);
})
this.onRejectedQueue.push(val= >{
onRejected && onRejected(val);
reject(val);
})
// reject("err")})}Copy the code
Promise other static methods
- promise.resolve
- promise.reject
- promise.catch
- promise.all
- promise.race
- promise.finally
catch(onRejected){
this.then(undefined,onRejected);
}
static resolve(val){
return new KPromise(resolve= >{ resolve(val); })}static reject(val){
return new KPromise((resolve,reject) = >{ reject(val); })}static all(lists){
let arr = [];
return new KPromise((resolve) = >{
lists.forEach(item= >{
item.then(res= >{
arr.push(res);
if(arr.length===lists.length){ resolve(arr); }})})})}static race(lists){
return new KPromise((resolve,reject) = >{
lists.forEach(item= >{
item.then(res= >{
resolve(res);
},err= >{ reject(err); })})})}finally(cb) {
this.then(cb, cb);
}
Copy the code
github
portal