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