[es6] Talk about Promise

It has been a long time since I learned vue to write projects. Promise is used in many places in the project. No matter the front-end and back-end interface communication, or various third-party component libraries or plug-ins, the shadow of Promise will be found more or less, so today is simple Sort out the knowledge system about Promise.

Table of contents

1. What is Promise

2. About the callback function

1.1. Synchronous call callback function

1.2. Callback function called asynchronously

3. Error handling about js

1.1. Error: the parent type of all errors

1.2. ReferenceError: The referenced variable does not exist

1.3. TypeError: type error

1.4. RangeError: The value is not within its allowed range

1.5. SyntaxError: syntax error

2.1. Catch errors: try...catch

2.2. Throwing an error: throw error

4. Implement Promise encapsulation XMLHttpRequest

5. Advantages of Promise

1. The way to specify the callback function is more flexible

2. Support chain calls to solve callback hell

6. Promise API

0. Promise constructor

1.Promise.prototype.then

2.Promise.prototype.catch

3.Promise.resolve

4.Promise.reject

5.Promise.all

6.Promise.race

7.Promise.allSettled

Seven. Promise in-depth

1. How to change the state of the promise?

2. A Promise specifies multiple success or failure callbacks, will they all be called?

3. Who should change the Promise state and specify the callback first?

4. What determines the state of the new Promise returned by promise.then?

5. Promise abnormal penetration

6. Break the Promise chain

7. How to ensure that Promise.all always returns a successful status

Eight. Custom Promise

1. Define the Promise constructor

1) Define the properties of the constructor

2) Call the executor and catch the exception

3) Modify the state of Promise

2. Define the then method

1) Determine the state of the current Promise instance 

2) Determine the Promise status returned by the then method

3) Specify the default value of the callback function

3. Define the catch method

4. Define the resolve method

5. Define the reject method

6. Define the all method

7. Define the race method

8. Define the allSettled method

Nine.js macro queue and micro queue


1. What is Promise

A solution for asynchronous programming instead of a pure callback (abstraction); it is a constructor (syntax); it encapsulates an asynchronous operation and can get the result (function);

Promise has three states, pending undetermined, resolved successfully, rejected failed ;

Promise state changes, pending becomes resolved, and pending becomes rejected. It should be noted that the state of a Promise object can only be changed once .

2. About the callback function

Before formally talking about Promise, it is necessary to review the concept of callback functions, because there will be a large number of callback functions in Promise programming. Callback functions, different from ordinary functions, need to meet several conditions:

1. It is defined by the developer himself

2. Will not actively call

3. Will eventually be executed

Callback functions are generally divided into two categories, synchronous callbacks and asynchronous callbacks:

1.1. Synchronous call callback function

Execute as soon as it comes up, it will not be placed in the queue

const arr = [1,2,3];
arr.forEach(item => {
    console.log(item); // 同步回调函数,先打印
)
console.log('forEach执行完毕');  // 后打印

1.2. Callback function called asynchronously

Will be put into the queue and executed after the synchronous code is executed

setTimeout(() => {
    console.log('setTimeout callback');  // 异步回调函数,后执行
});
console.log('setTimeout after');   // 先执行

3. Error handling about js

There will be a success or failure callback in the Promise. In the failure callback, the js error will be used to handle the failure result. Here is a summary.

js common error types:

1.1. Error : the parent type of all errors

1.2. ReferenceError : The referenced variable does not exist

 Enter the following code in the browser console

console.log(a)

The following error will appear on the console, which is caused by you referencing a variable a that does not exist 

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA4KaP546W5Ly0,size_20,color_FFFFFF,t_70,g_se,x_16

1.3. TypeError: type error

 Enter the following code in the browser console

let b = null;
console.log(b.xxx);

 The premise of reading the xxx attribute on b is that b must be an object, but b is null and not an object, and a type error is reported

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA4KaP546W5Ly0,size_20,color_FFFFFF,t_70,g_se,x_16

1.4. RangeError: The value is not within its allowed range

function fn() {
    fn();  // 函数递归调用
}

fn();  

 Call itself recursively in the fn function, but a limit on the number of calls is required. The limit is exceeded due to no defined limit, so an error of exceeding the range is reported.

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA4KaP546W5Ly0,size_20,color_FFFFFF,t_70,g_se,x_16

1.5. SyntaxError: syntax error

const a = """";

 Double quotes cannot be wrapped within double quotes, this is a syntax error

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA4KaP546W5Ly0,size_20,color_FFFFFF,t_70,g_se,x_16

When an error occurs in the js code, the program cannot continue to execute downwards. At this time, the program needs to continue to be executed downwards, and the error needs to be handled. There are two methods:

2.1. Catch errors: try...catch

Put the wrong code in the try, catch the exception, get the thrown exception object error in the catch, it has two attributes error.message (wrong information) and error.stack (wrong information and error location, That is, the function call stack records information) . After this, the code can be executed normally.

try{
    let a;
    a.xxx;
} catch(error) {
    console.log(error);
}

2.2. Throwing an error: throw error

function judgeTime() {
    if(new Date() % 2 !== 0) {
        console.log('奇数,执行');
    } else {
        throw new Error('偶数,无法执行');  // 抛出异常
    }
}

try{
    judgeTime();   // 捕获异常
} catch(error) {
    alert(error.message);
}

4. Implement Promise encapsulation XMLHttpRequest

0. Use Promise to encapsulate native XMLHttpRequest to send a request to the specified interface https://api.apiopen.top/getJoke to obtain response data;

1. Instantiate the promise object new Promise. The parameter of the object is a value of a function type . The function has two formal parameters . The names of the formal parameters can be customized, usually resolve and reject;

2. After getting the data, you can call the formal parameter resolve or reject function to change the state of the promise , and the state changes to success (three states: initialization, success, and failure). When the status code of the XMLHttpRequest instance is 200, the response is successful, and the resolve method is called. It indicates that the status returned by this Promise instance is successful; when the status code of the XMLHttpRequest instance is not [200,300], the response to this request fails, and the reject method is called, indicating that the status returned by this Promise instance is failed.

3. Then call the then method of the promise object, accepting two parameters, both of which are function type values , each function has a formal parameter, the successful formal parameter is generally called value, and the failed formal parameter is called reason; when the promise object state If it is successful, that is, when resolve is called asynchronously, the then method will execute the code of the first callback function; when the state of the promise object is failed, that is, when reject is called asynchronously, then method will execute the code of the second callback function.

const p = new Promise((resolve,reject) => {
    //1.创建对象
    const xhr = new XMLHttpRequest();

    //2.初始化
    xhr.open('GET','https://api.apiopen.top/getJoke');

    //3.发送
    xhr.send();

    //4.绑定事件,处理相应结果
    xhr.onreadystatechange = function(){
        //判断
        /*on 当...时候
         *readystate是xhr对象中的属,表示状态,有0 1 2 3 4
            0:表示未初始化
            1:open方法调用完毕
            2:send方法调用完毕
            3:服务端返回了部分的结果
            4:服务端返回了所有的结果
            一共有五个值,总共会触发4次
         *change 改变
         */
        if (xhr.readyState == 4) {
            //判断相应状态码 200 -299 为成功的
            if (xhr.status >= 200 && xhr.status <=300) {
                //表示成功
                resolve(xhr.response);
            }else{
                //如果失败
                reject(xhr.status);
            }
        }
    }
});
//指定回调
p.then(function(value){
    // 这里的value获取的就是resolve的实参xhr.response,这里是接口的响应结果
    console.log(JSON.parse(value));  
},function(reason){
    // 这里的reason获取的就是reject的实参xhr.status,这里是响应失败的状态码
    console.error(reason);  
})

Console output:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA4KaP546W5Ly0,size_20,color_FFFFFF,t_70,g_se,x_16

5. Advantages of Promise

1. The way to specify the callback function is more flexible

Do not use Promise to execute asynchronous tasks, you must specify the success and failure callbacks before starting the asynchronous task;

After using Promise, you can execute the asynchronous task first, without specifying the callback for result processing, and you can specify it after the asynchronous result returns;

2. Support chain calls to solve callback hell

   What is callback hell? The callback function is called nestedly , and the result of the asynchronous execution of the external callback function is the condition for the execution of the nested callback function. Severe indentation and poor code readability.

6. Promise API

0. Promise constructor

The constructor needs to pass in an executor as a parameter, which is executed synchronously , and asynchronous operations are executed in the executor

The executor function needs to pass two parameters, the resolve and reject functions:

    resolve: called when the internal definition succeeds;

    reject: called when the internal definition fails;

1.Promise.prototype.then

Two callback functions onResolved and onRejected are required as parameters:

    onResolved function: a successful callback function, called when the Promise status is successful (value) => {}

    onRejected function: failure callback, called when the Promise status is failed (reason) => {}

Both callback functions return a new Promise object;

2.Promise.prototype.catch

Called when the internal definition fails, it is the syntactic sugar of Promise.prototype.then, which is equivalent to then(undefined,onRejected)

const p1 = new Promise((resolve,reject) => {
    setTimeout(() => {
        let rand = Math.ceil(Math.random() * 10);
        if(rand % 2 === 0) {
            resolve(rand);
        } else {
            reject('error');
        }
    },1000) 
});

p1.then(
    (value) => {
        console.log(value);
    }
)
.catch(
    (err) => {
        console.log(err);
    }
 );

3.Promise.resolve

Quickly yield a successful Promise

If you want to generate a Promise with a success value of 1, you can write it like this:

const p = new Promise((resolve,reject) => {
    resolve(1);
});   
p.then(value => {console.log(value)});  // 1

However, the above writing method is cumbersome and can be realized by directly calling the resolve method, which belongs to syntactic sugar

const p = Promise.resolve(1);
p.then(value => {console.log(value)});  // 1

4.Promise.reject

In the same way as above, if you want to quickly generate a Promise with a failure value of 1, you can directly call its static method reject

const p = Promise.reject(1);
p.catch(reason => {console.log(reason)});  // 1

5.Promise.all

An array needs to be passed as a parameter, and the elements of the array are multiple Promises

Return result: a new Promise

  • When there are failed Promises among these Promises, the final result returned is failure
  • When these Promises are all successful, the return result is success, and the value is an array, which are the asynchronous return results of these Promises, and the return order is consistent with the order of the array elements in all
const p1 = new Promise((resolve,reject) => {
    resolve(1);
});
const p2 = Promise.resolve(2);
const p3 = Promise.reject(3);
// const pAll = Promise.all([p1,p2,p3]);
const pAll = Promise.all([p1,p2]);
pAll.then(
    values => {
        console.log(values);  // [1,2]
    },
    reason => {
        console.log(reason);  // 3
    }
);

6.Promise.race

The parameter contains an array of n Promises

The return is a new Promise, the result state of the first fulfilled Promise is the final result state

const p1 = new Promise((resolve,reject) => {
    setTimeout(() => {
        resolve(1);
    },1000);
});
const p2 = Promise.resolve(2);
const p3 = Promise.reject(3);
const race = Promise.race([p1,p2,p3]);
race.then(
    value => {
        console.log(value);  // 2
    },
    reason => {
        console.log(reason);  
    }
);

Although the parameters of the race are p1, p2, and p3, there is a one-second delay in p1, so p2 is the first promise to be completed, and the final result of the race is the result of p2.

7.Promise.allSettled

The condition for the Promise.all method to resolve is that all Promise instances in the parameter array need to be resolved, which is obviously not applicable in some businesses. If a module needs to display three parts of content, each part of the content has a return Promise instance Interface, if Promise.all is used, all three interfaces must successfully return data. If one interface is down, the data returned by the other two interfaces cannot be obtained, because the catch method has been entered at this time and cannot be used in the Operation data, rendering interface, etc. in the successful callback function.

At this point Promise.allSettled comes in handy. Regardless of whether the parameter instance rejects or not, Promise.allSettled will eventually resolve internally , but a status status will be added to record whether the corresponding parameter instance is executed successfully. We can filter out the rejected data based on this state, and only manipulate the fulfilled data to get the business logic we want.

var promise1 = new Promise(function(resolve,reject){
  setTimeout(function(){
    reject('promise1')
  },2000)
})
			
var promise2 = new Promise(function(resolve,reject){
  setTimeout(function(){
    resolve('promise2')
  },3000)
})
 
var promise3 = Promise.resolve('promise3')
 
var promise4 = Promise.reject('promise4')
 
// 该函数接受一个 promise 数组(通常是一个可迭代对象)作为参数:
Promise.allSettled([promise1,promise2,promise3,promise4])
.then((args) => {
  console.log(args);
  // 当所有的输入 promises 都被 fulfilled 或 rejected 时,
  // statusesPromise 会解析为一个具有它们状态的数组
  /*
  result: 
  [
    {"status":"rejected","reason":"promise1"}, 
    {"status":"fulfilled","value":"promise2"},
    {"status":"fulfilled","value":"promise3"}, 
    {"status":"rejected","reason":"promise4"}
  ]*/
})

Processing of the results returned by allSellted: 

//过滤出成功的请求
const successfulPromises = result.filter(p => p.status === 'fulfilled');

const errors = result
    .filter(p => p.status === 'rejected')
    .map(p => p.reason);//过滤出失败的请求,并输出原因

Seven. Promise in-depth

1. How to change the state of the promise?

1) resolve: If it is currently pending, it will become resolved

2) reject: If it is currently pending, it will become rejected

3) An exception is thrown: if it is currently pending, it will become rejected

const p = new Promise((resolve,reject) => {
    throw new Error('error');
)

2. A Promise specifies multiple success or failure callbacks, will they all be called?

     will be called, and can perform different operations in multiple failure/success callbacks

const p1 = new Promise((resolve,reject) => {
    resolve(1);
});
p1.then(
    value => {console.log('1',value)},
    reason => {}
);
p1.then(
    value => {console.log('2',value)},
    reason => {}
);

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA4KaP546W5Ly0,size_20,color_FFFFFF,t_70,g_se,x_16

3. Who should change the Promise state and specify the callback first?

    It's all possible. Under normal circumstances, the callback function is specified first, and then the state is modified, but it is also possible to change the state first and then specify the callback.

  •  How to change the state first and then specify the callback?

    Call resolve and reject directly in the executor, and call then after a longer delay

Conventional usage: set the callback first, then change the state


new Promise((resolve,reject) => {
    setTimeout(() => {
        // 2.后改变状态,异步执行回调函数
        reolve(1);
    },1000)
}).then(
    // 1. 先指定回调
    value => {},
    reason => {}
)

 Change the state, and then specify the callback

// Promise内代码为同步
new Promise((resolve,reject) => {
    // 1.先改变状态(同步代码)
    resolve(1);
}).then(
    // 2. 指定回调
    value => {},
    reason => {}
);

// Promise内代码为异步
const p = new Promise((resolve,reject) => {
    setTimeout(() => {
        resolve(1);
    },1000)
});
setTimeout(() => {
    p.then(
        value => {},
        reason => {}
    )
},1100);
  • When do you get the data?

If the callback is specified first, then when the state changes, the callback function will be called to get the data;

If the state is changed first, then when the callback is specified, the callback function will be called to get the data;

4. What determines the state of the new Promise returned by promise.then?

Determined by the execution result of the callback function specified by then()

1) If an exception is thrown, the new Promise becomes rejected, reason is the thrown exception

2) If any non-Promise value is returned, the new Promise becomes resolved, and the value is the returned value

3) If another new Promise is returned, the result of this Promise is the result of the new Promise

Throw an exception 

new Promise((resolve,reject) => {
    resolve(1);
}).then(
    value => {
        console.log(value);   // 1
        throw 2;
    },
    reason => {
        console.log(reason)
    }
).then(
    value => {
        console.log(value)
    },
    reason => {
        console.log(reason);   // 2
    }
)

 returns a non-Promise (one)

new Promise((resolve,reject) => {
    resolve(1);
}).then(
    value => {
        console.log(value);  // 1
        return 2;
    },
    reason => {
        console.log(reason)
    }
).then(
    value => {
        console.log(value);  // 2
    },
    reason => {
        console.log(reason);  
    }
)

Return non-Promise (second), if you do not manually write the return value, it will return undefined by default

new Promise((resolve,reject) => {
    reject(1);
}).then(
    value => {
        console.log(value); 
    },
    reason => {
        console.log(reason);  // 1
    }
).then(
    value => {
        console.log(value);  // undefined
    },
    reason => {
        console.log(reason);  
    }
)

Returns the Promise type. At this time, what is returned is a successful Promise, then the execution result of the then callback is a successful Promise

new Promise((resolve,reject) => {
    resolve(1);
}).then(
    value => {
        console.log(value);  // 1
        return Promise.resolve(2);
    },
    reason => {
        console.log(reason)
    }
).then(
    value => {
        console.log(value);  // 2
    },
    reason => {
        console.log(reason);  
    }
)

5. Promise abnormal penetration

When using Promise's then chain call, you can specify the failed callback catch at the end; any exception in the previous operation will be passed to the last failed callback for processing, for example:

new Promise((resolve,reject) => {
    reject(1);
}).then(
    value => {
        return 2;
    }
).then( 
    value => {
        return 3
    }
).catch(
    reason => {
        console.log(reason);  // 1
    }
)

A Promise instance with a failure result is created, and the failure result will be directly captured by the final catch, but the premise is that the then chain call in the code cannot write a failure callback, as shown in the above code. If the failure callback of the then method is written, the result of the failure will be captured and processed by the failure callback, and will not be processed in the catch.

If you do not write a failed callback, the bottom layer of Promise will add a failed callback by default and throw an exception, as follows:

new Promise((resolve,reject) => {
    reject(1);
}).then(
    value => {
        return 2;
    },
    // reason => {throw reason}   --->   不写失败的回调会默认加上的代码
).then( 
    value => {
        return 3
    },
    // reason => {throw reason}
).catch(
    reason => {
        console.log(reason);  // 1
    }
)

Only in this way can exception penetration be achieved. Of course, if you insist on writing a failed callback and want catch to handle the failed result, you can write it like this:

new Promise((resolve,reject) => {
    reject(1);
}).then(
    value => {
        return 2;
    },
    reason => Promise.reject(reason)   
).then( 
    value => {
        return 3
    },
    reason => Promise.reject(reason) 
).catch(
    reason => {
        console.log(reason);  // 1
    }
)

6. Break the Promise chain

When the Promise chain is called, it will be interrupted in the middle, and the subsequent callback function will not be called; a Promise object with a state of pending can be returned to the callback function, that is,  new Promise(() => {}) 

new Promise((resolve, reject) => {
    reject(1);
}).then(
    value => {
        return 2;
    },
    reason => {
        return new Promise(() => {});
    }
).then(
    value => {
        return 3
    },
    reason => new Promise(() => {})
)

7. How to ensure that Promise.all always returns a successful status

The condition for Promise.all to return success is that each Promise is a successful return result. If there is a failure, it will not return a successful status, so you can use catch to capture the failed results of these Promise instances and return a non-Promise type value. , so that each Promise state handled by all is guaranteed to be successful.

const a = Promise.resolve(1);
const b = Promise.reject(new Error(2));
const c = Promise.resolve(3);

const arr = [a, b, c].map(p => p.catch(e => e));

Promise.all(arr)
    // [1, Error: 2 at http://127.0.0.1:5500/es6/e12.html:164:34, 3]
    .then(results => console.log(results)) 
    .catch(e => console.log(e));

Eight. Custom Promise

1. Define the Promise constructor

In the constructor, you need to do the following operations:

1) Define the properties of the constructor

2) Call the executor and catch the exception

3) Modify the state of Promise and perform related operations (ie call resolve and reject)

function Promise(excutor) {
    // 属性值
    ......

    // 定义成功和失败的方法
    resolve() {......}
    reject() {......}

    // 调用执行器
    excutor(resolve,reject);
}

1) Define the properties of the constructor

First define the Promise constructor, and first add attributes to the constructor , which are the state of Promise, PromiseState, the return result of Promise, PromiseResult, and the callback function callback defined in the then method. As mentioned above, a Promise instance can specify multiple Success or failure callback, so the callback attribute needs to be written in the form of an array ;

this.PromiseState = 'pending';
this.PromiseResult = null;
this.callback = [];

2) Call the executor and catch the exception

Then define and call the executor . The executor is the parameter passed when the Promise is instantiated. It is a callback function. The callback function has two parameters, corresponding to resolve and reject respectively;

One more thing to consider is the problem of throwing exceptions in the executor . Since the Promise state is a failure after the Promise executor throws an exception, it is necessary to use try...catch to catch the exception and call the reject method just defined to modify the state .

 try{
     //同步调用执行器函数,try catch要加在这外面,这样才能捕获throw的内容
     executor(resolve,reject);
 } catch(e){
     //修改Promise对象状态为失败
     reject(e);
 }

3) Modify the state of Promise

The next step is to define the resolve and reject methods . The implementation methods of the two methods are similar. What needs to be done is to modify the state of the current Promise instance, that is, the value of PromiseState, and obtain the result data PromiseResult. Finally, execute the corresponding callback function callback, because Executing the callback function is an asynchronous operation , so use setTimeout to wrap it, and place the operation in the message object for asynchronous execution;


    function resolve(data){
        //判断当前的状态,确保Promise只能修改一次状态
        if (self.PromiseState !== "pending") return; 
        self.PromiseState = 'fulfilled';
        self.PromiseResult = data;
        //异步操作执行完毕后执行
        self.callback.forEach(item =>{
            //将then方法中的回调改为异步执行
            setTimeout(() => {
                item.onResolved(data);
            })
        });
    }
    function reject(data){......}
  function Promise(executor){
    //添加属性
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    this.callback = [];
    const self = this;

    function resolve(data){
        //判断当前的状态,确保Promise只能修改一次状态
        if (self.PromiseState !== "pending") return; 
        self.PromiseState = 'fulfilled';
        self.PromiseResult = data;
        //异步操作执行完毕后执行
        self.callback.forEach(item =>{
            //将then方法中的回调改为异步执行
            setTimeout(() => {
                item.onResolved(data);
            })
        });
    }
    function reject(data){
        if (self.PromiseState !== "pending") return; 
        self.PromiseState = 'rejected';
        self.PromiseResult = data;
        //异步操作执行完毕后执行,改变状态后再去执行回调
        self.callback.forEach(item =>{
            setTimeout(() => {
                item.onRejected(data);              
            })
        });

    }
    try{
        //同步调用执行器函数,try catch要加在这外面,这样才能捕获throw的内容
        executor(resolve,reject);
    } catch(e){
        //修改Promise对象状态为失败
        reject(e);
    }
}

2. Define the then method

Executing the then method mainly needs to perform the following operations:

1). Judge the state of the current Promise instance

2). Determine the status of the Promise returned by the then method

3). Specify the default value of the callback function

1) Determine the state of the current Promise instance 

As mentioned above, when the then method is executed, the current state of the Promise may be pending, resolve, or reject. This is determined by the order in which the Promise instance changes the state first or executes the callback first. If the Promise encapsulates a Network request, since the request is asynchronous, and the execution of the then method is synchronous, so at this time, the then callback is specified first, and then the state is modified, then the state of executing the then method is pending, and the callback function needs to be saved in the callback at this time , and then execute the success/failure callback of then after the asynchronous operation is executed, and the execution success or failure callback is executed in the Promise constructor.

if (this.PromiseState === 'pending') {
    //保存回调函数
    this.callback.push({
        onResolved:function(){
            callback(onResolved);
        },
        onRejected:function(){
            callback(onRejected);
        }
    });
}

If the state is modified first, and then the callback is executed, then the state of the Promise instance of the then method is fulfilled or rejected, then the corresponding successful or failed then callback needs to be executed, and the state of the Promise returned is determined according to the return value of the callback

if (this.PromiseState === 'fulfilled') {
    //将then方法中的回调改为异步执行
    setTimeout(() => {
        callback(onResolved);
    })
}
if (this.PromiseState === 'rejected') {
    setTimeout(() => {
        callback(onRejected);
    })
}

2) Determine the Promise status returned by the then method

The then method has a return value and is a Promise. The result of the returned promise is determined by the execution result of onResolved/onRejected:

 1. If an exception is thrown, the returned Promise result is a failure, and the reason is an exception;

 2. If the return is a Promise, the result of the returned Promise is this result;

 3. If the returned Promise is not a Promise, the returned Promise is successful, and value is the return value.

The callback method defined here is used to handle the mapping relationship between the execution results of onResolved/onRejected and the results returned by the then method.

return new Promise((resolve,reject) =>{
    function callback(type) {
        try{
            //获取回调函数的执行结果
            let result = type(self.PromiseResult);
            //判断
            if (result instanceof Promise) {
                //当回调返回的是一个Promise实例时
                result.then(v => {
                    //result成功调用resolve方法,让返回的Promise也成功
                    resolve(v);
                },r => {
                    //result失败调用reject方法,让返回的Promise也失败
                    reject(r);
                });
                // result.then(resolve,reject);
            } else {
                //当返回的是基本数据类型时
                //默认成功调用resolve方法
                resolve(result);
            }
        }catch(e){
            reject(e);
        }
    }
}

3) Specify the default value of the callback function

Specifying the default value of the failure callback is to solve the problem of exception penetration; in addition, the encapsulation of the catch is also implemented based on the then method, and the catch method can continue to be chained through .then, so the default value of the success callback must also be specified. Pass successful results down the line.

if (typeof onRejected !== 'function') {
    //如果then方法的第二个参数不传递,就默认传递以下这个回调函数
    onRejected = reason => {
        throw reason;
    }
}
if (typeof onResolved !== 'function') {
    onResolved = value => value;
}

 Full code: 

Promise.prototype.then = function(onResolved,onRejected){
    let self = this;
    //判断回调函数的第二个参数,不是一个函数就抛出异常,解决异常穿透
    if (typeof onRejected !== 'function') {
        //如果then方法的第二个参数不传递,就默认传递以下这个回调函数
        onRejected = reason => {
            throw reason;
        }
    }
    if (typeof onResolved !== 'function') {
        onResolved = value => value;
    }
    //then方法返回的是一个promise对象
    return new Promise((resolve,reject) =>{
        function callback(type) {
            try{
                //获取回调函数的执行结果
                let result = type(self.PromiseResult);
                //判断
                if (result instanceof Promise) {
                    //当回调返回的是一个Promise实例时
                    result.then(v => {
                        //result成功调用resolve方法,让返回的Promise也成功
                        resolve(v);
                    },r => {
                        //result失败调用reject方法,让返回的Promise也失败
                        reject(r);
                    });
                    // result.then(resolve,reject);
                } else {
                    //当返回的是基本数据类型时
                    //默认成功调用resolve方法
                    resolve(result);
                }
            }catch(e){
                reject(e);
            }
        }
        //调用成功的回调函数
        if (this.PromiseState === 'fulfilled') {
            //将then方法中的回调改为异步执行
            setTimeout(() => {
                callback(onResolved);
            })
        }
        if (this.PromiseState === 'rejected') {
            setTimeout(() => {
                callback(onRejected);
            })
        }
        //执行的是异步任务,当状态依旧是是pending时,先要保存onResolved,onRejected
        //到callback属性中,当异步操作执行完毕后再执行这两个回调函数
        if (this.PromiseState === 'pending') {
            // debugger;
            //保存回调函数
            this.callback.push({
                onResolved:function(){
                    callback(onResolved);
                },
                onRejected:function(){
                    callback(onRejected);
                }
            });
        }
    })

}

3. Define the catch method

The catch method is actually a secondary encapsulation based on then. It should be noted that after the catch, the chain call of the then method can still be used , so the encapsulated then method needs to be returned as the return value.

Promise.prototype.catch = function(onRejected) {
    return this.then(undefined,onRejected); 
}

4. Define the resolve method

Returns a success/failure Promise

If the parameter is a general value, the Promise succeeds, and the value is this value;

If the parameter is a successful Promise, the Promise succeeds, and the value is the value of the Promise

If the parameter is a failed Promise, the Promise fails and reason is the reason of the Promise

//添加resolve方法,该方法属于Promise函数对象的,不是实例对象,无需在原型上添加
Promise.resolve = function(value) {
    return new Promise((resolve,reject) => {
        if (value instanceof Promise) {
            value.then(s => {
                resolve(s);
            }, f => {
                reject(f);
            });
        } else {
            resolve(value);
        }
    });
};

5. Define the reject method

Can only return a failed Promise

Promise.reject = function(value) {
    return new Promise((resolve,reject) => {
        reject(value);
    })
};

6. Define the all method

Promise.all = function(promises) {
    return new Promise((resolve,reject) => {
        let arr = []; // 定义一个数组,用来存放成功时返回的各个Promise的返回结果
        let count = 0; // 定义一个变量用于计数
        for (let i = 0; i < promises.length; i++) {
            Promise.resolve(promises[i]).then(s => {
                arr[i] = s;
                count++;
                //判断条件为真代表每一个Promise都是成功的结果,此时可以调用resolve方法
                if (count === promises.length) {
                    resolve(arr);
                }
            },f => {
                //只要存在失败的Promise就直接调用reject方法
                reject(f); 
            })            
        }
    })
};

7. Define the race method

Promise.race = function(promises){
    return new Promise((resolve,reject) => {
        for (let i = 0; i < promises.length; i++) {
            Promise.resolve(promises[i]).then(s => {
                resolve(s);
            },f => {
                reject(f);
            })            
        }
    })
}

8. Define the allSettled method

Promise.allSettled = function (promises) {
    return new Promise(resolve => {
      const data = [], len = promises.length;
      let count = len;
      for (let i = 0; i < len; i += 1) {
        const promise = promises[i];
        promise.then(res => {
          data[i] = { status: 'fulfilled', value: res };
        }, error => {
          data[i] = { status: 'rejected', reason: error };
        }).finally(() => { 
          if (!--count) {
            resolve(data);
          }
        });
      }
    });
  }

Nine.js macro queue and micro queue

What is synchronous and asynchronous?

Synchronization: Tasks are executed sequentially. If the previous task is not completed, the next task cannot be processed;

Asynchronous: If the previous thing is not completed, the next thing can also be processed.


In the actual use of Promise, asynchronous code is encapsulated in most cases, and they are executed after synchronous code. This is because js is a single-threaded mechanism . The following explains the principle of the order in which js code is executed:

The queue used to store and execute the callback function in js contains two different specific queues, namely the macro queue and the micro queue ;

The macro queue is used to save the macro tasks to be executed, including timer callbacks, DOM event callbacks, and ajax callbacks ;

Microtasks are used to save the queue of microtasks to be executed, including promise callbacks and MutationObserver callbacks;


js code execution sequence: when synchronous code is executed from top to bottom, it is found that asynchronous code will put it into the waiting task queue, and then the main thread will continue to execute downwards. The js engine will first execute all initialization synchronous codes. At this time, the main thread In the idle state, the main thread goes back to wait for the event queue to take out the asynchronous tasks that arrive at the time point one by one. Before taking out the first macrotask for execution, it needs to take out all the microtasks one by one for execution. (The microtask is executed first, and then the macrotask is executed). After one execution is completed, the next asynchronous task that needs to be executed is taken out. This entire process of repetition is called an event loop .

Note: In setTimeout, if the delay time is 0, it will not be executed immediately. The browser will have a minimum waiting time by default, which is about 10ms.

Guess you like

Origin blog.csdn.net/weixin_43655896/article/details/122802407