Home >Web Front-end >JS Tutorial >In-depth analysis of promise objects (with examples)

In-depth analysis of promise objects (with examples)

不言
不言forward
2018-11-23 14:49:002211browse

This article brings you an in-depth analysis of promise objects (with examples). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

Asynchronous in js was initially implemented using callback functions, so if asynchronous is nested, callback hell will occur, making the code difficult to read and maintain. Later, es6 appeared Using promises solves the problem of callback hell. Now we will write the code ourselves to implement the promise, so that we can have a deep understanding of the operating mechanism of the promise and be more comfortable using promises in the future. Before you start, you can take a look at promise's official website promise/A

Let's first look at the usage of promise

new Promise((resolve,reject)=>{
  resolve(1);
  reject(11);
}).then(res=>{
  console.log(res);
  setTimeout(()=>{
    return new Promise((resolve,reject)=>{
        resolve(2)
    }) 
  },1000)
}).then(res2=>{
  console.log(res2);
});

Console printing
1
...1s later
2

Let’s first analyze the above code and ask a few questions
1. The first paragraph has both resolve and reject, but only 1 is output. Why?
2.How does res in then get the value in resolve?
3.How does promise achieve chain calling?

State machine

There is a concept of state machine in promise. Let’s first talk about why there is a concept of state machine. Because the state of promise changes in one direction and has three states: pending. , fullfilled, rejected, and these three states can only be in the form of pending->fullfilled or pending->rejected, which means that after fullfilled is executed, rejected will not be executed. This explains the first question above.

Let’s take a look at the complete code of the specific implementation

const PENDING = 'PENDING';
const FULLFILLED = 'FULLFILLED';
const REJECTED = 'REJECTED';    
class Promise{
    constructor(fn){
       this.status = PENDING;//状态
        this.data = undefined;//返回值
        this.defercb = [];//回调函数数组
        //执行promise的参数函数,并把resolve和reject的this绑定到promise的this
        fn(this.resolve.bind(this),this.reject.bind(this));
    }
    resolve(value){
        if(this.status === PENDING){
            //只能pending=>fullfied
            this.status = FULLFILLED;
            this.data = value;
            this.defercb.map(item=>item.onFullFilled());
        }
    }
    reject(value){
        if(this.status === PENDING){
            //只能pending=>rejected
            this.status = REJECTED;
            this.data = value;
            this.defercb.map(item=>item.onRejected());
        }
    }
    then(resolveThen,rejectThen){
       //如果没有resolveThen方法,保证值可以穿透到下一个then里有resolveThen的方法中
        resolveThen = typeof resolveThen === 'function' ? resolveThen : function(v) {return v};
        rejectThen = typeof rejectThen === 'function' ? rejectThen : function(r) {return r};
        //返回的都是promise对象,这样就可以保证链式调用了
        switch(this.status){
            case PENDING:
            return new Promise((resolve,reject)=>{
                const onFullFilled = () => {
                    const result = resolveThen(this.data);//这里调用外部then的resolveThen方法,将值传回去
                    //如果返回值是promise对象,执行then方法,取它的结果作为新的promise实例的结果,因为this.data会重新赋值
                    result instanceof Promise && result.then(resolve,reject);
                }
                const onRejected = ()=>{
                    const result = rejectThen(this.data);
                    result instanceof Promise && result.then(resolve,reject);
                }
                this.defercb.push({onFullFilled,onRejected});
            });
            break;
            case FULLFILLED:
               return new Promise((resolve,reject)=>{
                     const result = resolveThen(this.data);
                     result instanceof Promise && result.then(resolve,reject);
                     resolve(result);
               })
            break;
            case REJECTED:
               return new Promise((resolve,reject)=>{
                   const result = rejectThen(this.data);
                   result instanceof Promise && result.then(resolve,reject);
                   reject(result)
               })   
            break;
        }
    }
}

Run the following example

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1);
    }, 1000);
}).then((res2) => {
    console.log(res2);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
        }, 1000);
    });
}).then((res3) => {
    console.log(res3);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
        }, 1000);
    });
}).then((res4) => {
    console.log(res4);
});

Console printing
...1s later
1
...1s later
2
...1s later
3
It means that there is no problem with the above implementation
But there is another problem, which is the order of the event loop. For example, executing the following code

new Promise((resolve) => {
    resolve();
})
.then(() => {
    console.log('1');
})
.then(() => {
    console.log('2');
});
console.log('3');

does not output 3,1,2 as expected, but outputs 1,2,3. The reason is because our Promise is in the main thread and not in the next one. In the task queue, settimeout can be added to solve this problem, but this is just to let us better understand the execution sequence. However, in fact, promises belong to micro tasks, while settimeout belongs to macro tasks, which is still different.

The above is the detailed content of In-depth analysis of promise objects (with examples). For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete
Previous article:What is jqueryNext article:What is jquery