目 录CONTENT

文章目录

JS高阶(一)Promise

Dioxide-CN
2022-01-08 / 1 评论 / 2 点赞 / 88 阅读 / 10,205 字
温馨提示:
本文最后更新于 2022-04-23,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

Promise是什么?

  1. 抽象表达:
    • Promise是ES6中新增的规范;
    • Promise是js中异步编程的新解决方案(旧方案采用函数回调);
  2. 具体表达:
    • 从语法上说:Promise是一个构造函数
    • 从功能上说:Promise对象用来封装一个异步操作并可获取其成功/失败的值

为什么要使用Promise?

  1. 指定回调函数的方式更加灵活
    • 旧:必须在启动异步任务前指定
    • promise:启动异步任务=>返回promise对象=>给promise对象绑定回调函数(甚至可以在异步任务结束后指定多个)
  2. 支持链式调用,解决回调地狱问题
    • 回调地狱:回调函数嵌套调用,外部回调函数异步执行的结果是嵌套回调执行的条件;
    • 回调地域缺点:不便于阅读,不便于异常处理;
    • 解决方案:promise链式调用;

5.2.1 对象状态改变『PromiseState』

  1. pending 变更为 resolved;
  2. pending 变更为 reijected;

状态:

  • 实例对象中的一个属性『PromiseState』
  • 状态包含3种:pending(未定态)、resolved / fulfilled(成功)、rejected(失败)

说明:

  • 只有这2种,且一个promise对象只能改变一次
  • 无论变为成功还是失败都会有一个结果数据
  • 成功结果数据一般为value,失败结果数据一般为reason
//返回为Promise{<pending>}  
const p = new Promise((resolve, reject) => {  
    setTimeout (() => {  
        let n = 50;  
 if(n <= 30){  
            resolve(n);  
 } else {  
            reject(n);  
 }  
    }, 2000)  
});  
console.log(p);

5.2.2 对象结果值的属性『PromiseResult』

作用:存储对象失败或成功的结果;
修改:resolve、reject 函数可以修改 result 的值;

基本流程

//创建promise对象  
let p = new Promise((resolve, reject) => {  
    fs.readFile('./demo.txt', (err, data) => {  
        if(err) reject(err); //失败回调reject方法
 		resolve(data);  	 //成功回调reason方法
 });  
});  
//调用then方法  
p.then(value => {  //成功
    console.log(value.toString());  
}, reason => {     //失败
    console.log(reason);  
})

使用Promise

5.4.1 API
  1. **Promise 构造函数 ** Promise(executor){}

    • **executor 函数:**执行器 (resolve, reject) => {}
    • **resolve 函数:**定义内部成功时回调函数 value => {}
    • **reject 函数:**定义内部失败时回调函数 reason => {}
    • 说明: executor会在promise内部立刻同步调用,异步操作在执行器中执行;
  2. **Promise.prototype.then 方法 ** (onResolved, onRejected) => {}

    • onResolved 函数: 成功回调函数 (value) => {}
    • onRejected 函数: 失败回调函数 (reason) => {}
      -说明: 指定用于得到成功value的成功回调和用于得到失败reason的失败回调返回一个新的promise对象
  3. **Promise.prototype.catch 方法 ** (onRejected) => {}

    • onRejected函数:失败时回调的函数 (reason) => {}
//创建promise对象  
let p = new Promise((resolve, reject) => {  
    reject('error');  
});  
//调用catch方法  
p.catch(reason => {  
    console.log(reason);  
})

属于 Promise 函数,不属于任何一个实例化的对象;
用于快速得到一个 promise 对象;

resolve 方法

Promise.resolve 方法 (value) => {}

  • value:成功的数据或 promise 对象;
  • 说明:返回一个成功/失败的 promise 对象;
//创建promise.resolve对象  
//如果传入的对象为 非promise对象 则返回的是一个 成功的promise对象  
//如果传入的对象 非promise对象 则参数的结果决定了 resolve的结果  
let p = Promise.resolve();  
let p2 = Promise.resolve(new Promise( (resolve, reject) => {  
    // resolve('OK');  
 reject('ERROR');  
}));  
p2.catch(reason => {  
    console.log(reason);  
});

reject 方法

Promise.reject 方法 (reason) => {}

  • value:成功的数据或 promise 对象;
  • 说明:返回一个失败的 promise 对象;
//创建promise.resolve对象  
//如果传入的对象为 非promise对象 则返回的是一个 成功的promise对象  
//如果传入的对象 非promise对象 则参数的结果决定了 resolve的结果  
let p = Promise.reject(521);  
let c = Promise.reject(new Promise(resolve => {  
 resolve('OK');  
}))  
console.log(p);  
console.log(c);

all方法

Promise.all 方法 (promises) => {}

  • promises:包含 n 个 promise 对象的数组;
  • 说明: 返回一个新的 promise ,只有所有的 promise 都成功才返回成功,只要有一个失败则返回失败;
let p1 = new Promise((resolve) => {  
 resolve('OK');  
});  
let p2 = Promise.resolve('OK');  
let p3 = Promise.resolve('oh yeah');  
const result = Promise.all([p1, p2, p3]);  
console.log(result); //return true

race方法

Promise.race 方法 (promises) => {}

  • promises:包含 n 个 promise 对象的数组;
  • 说明: 返回一个新的 promise ,第一个完成的 promise 的结果状态就是最终的结果状态;
let p1 = new Promise((resolve) => {  
 resolve('OK');  
});  
let p2 = Promise.resolve('OK');  
let p3 = Promise.resolve('oh yeah');  
const result = Promise.race([p1, p2, p3]);  
console.log(result); //return p1 result

Promise内的关键问题

  1. 如何改变 promise 的状态?
    • resolve(value):如果当前为pending则会改变为resolve状态;
    • reject(reason):如果当前为pending则会改变为rejected状态;
    • 抛出异常:如果当前为pending则会改变为rejected状态;
let p = new Promise((resolve, reject) => {  
 //resolve函数  
 resolve('ok'); // pending -> fulfilled(resolved)  
 reject('error'); // pending -> rejected  
 throw 'sth. wrong'; //抛出错误(需要一个失败回调)  
});
  1. 一个 promise 函数指定多个 成功/失败 回调函数,都会调用吗?
    • 当 promise 改变为对应状态时,都会调用
let p = new Promise((resolve, reject) => {  
 //resolve函数  
 resolve('ok');  
});  
//指定回调 - 1p.then(value => {  
 console.log(value);  
});  
//指定回调 - 2p.then(value => {  
 alert(value);  
});
  1. 改变 promise 状态和指定回调函数谁先谁后?
    • 都有可能:正常情况下先指定回调函数后再改变状态,但也可以先改变状态再指定回调;
    • 如何先改变状态再指定回调?
      • 在执行器中直接调用 resolve() / reject() 函数;
      • 延迟更长时间才调用 then();
    • 什么时候才能得到数据?
      • 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据;
      • 如果先改变状态,那当指定回调时,回调函数就会调用,得到数据;
let p = new Promise((resolve, reject) => {  
 setTimeout(() => { //异步执行  
 resolve('ok');  
    }, 1000);  
});  
p.then(value => {  
 console.log(value);  
}, reason => {  
 console.log(reason);  
});
  1. promise.then()返回新的 promise 状态由谁决定?
    • 简单表达:由then指定的回调函数执行的结果决定;
    • 详细表达:
      • 如果抛出异常,新的 promise 变为 rejected,reason 为抛出的异常;
      • 如果返回任意非 promise 值,新 promise 变为resolved,value 为返回的值;
      • 如果返回另一个新的 promise,此 promise 的结果就会成为新 promise 的结果;
let p = new Promise((resolve, reject) => {  
 resolve('ok');  
});  
let result = p.then(value => {  
 // console.log(value);  
 // 1. 抛出异常  
 // throw 'wrong'; // 失败状态  
 // 2. 返回结果是非promise对象  
 // return 521; // 成功状态  
 // 3. 返回一个promise对象  
 return new Promise((resolve, reject) => {  
 resolve('success'); // 将该结果返回至result  
 });  
}, reason => {  
 console.warn(reason);  
});  
console.log(result);
  1. promise 串联多个操作任务
    • promise 的 then() 返回一个新的 promise,可以展开 then() 的链式调用;
    • 通过 then() 的链式调用可以串联多个 同步/异步 任务;
//规避回调地狱
let p = new Promise((resolve, reject) => {  
 setTimeout(() => {  
 resolve('ok');  
    }, 1000);  
});  
let result = p.then(value => {  
 return new Promise((resolve, reject) => {  
 resolve('success'); // 将该值回调至被串联的下一个then方法  
 });  
}).then(value => {  
 console.log(value); //success  
}).then(value => {  
 console.log(value); //undefined -> 上一个then成功但返回了undefined  
});
  1. promise 异常穿透
    • 当使用 promise 的 then 链式调用时,可以在最后指定失败的回调;
    • 在前部出现的所有异常都会穿透至最后的失败回调中;
let p = new Promise((resolve, reject) => {  
 setTimeout(() => {  
 reject('err');  
    }, 1000);  
});  
let result = p.then(value => {  
 return new Promise((resolve, reject) => {  
 resolve('success');  
    });  
}).then(value => {  
 console.log(value);  
}).then(value => {  
 console.log(value);  
}).catch(reason => {  
 console.warn(reason); //捕获所有异常并抛出  
});
  1. 中断 promise 链
    • 当使用 promise 的 then 链式调用时,在中间中断,不再调用后面的函数;
    • 方法:在回调函数中返回一个状态为 pending 的 promise 对象;
let p = new Promise((resolve, reject) => {  
 setTimeout(() => {  
 resolve('ok');  
    }, 1000);  
});  
let result = p.then(value => {  
 return new Promise((resolve, reject) => {  
 resolve('success');  
    });  
}).then(value => {  
 console.log(value);  
    return new Promise(() => {}); //中断promise链  
}).then(value => {  
 console.log(value);  
}).catch(reason => {  
 console.warn(reason); //捕获所有异常并抛出  
});

重写 promise 底层函数并实现所有功能

5.8 定义整体结构并封装成class类
class Promise{
    // 构造方法
    // executor:内部同步执行的函数 (resolve, reject) => {}
    constructor(executor) {
        // 添加并初始化属性
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        this.callbacks = [];
        // 保存实例对象的 this 的值:self _this that
        const self = this;

        // 声明resolve函数
        function resolve(data){
            // 判断状态
            if(self.PromiseState !== 'pending') return;
            // 修改对象状态 ( promiseState )
            self.PromiseState = 'fulfilled';
            // 设置对象结果值 ( promiseResult )
            self.PromiseResult = data;
            // 调用成功的回调函数 -> 异步
            setTimeout(() => {
                self.callbacks.forEach(item => {
                    item.onResolved(data);
                });
            });
        }
        // 声明reject函数
        function reject(data){
            // 判断状态
            if(self.PromiseState !== 'pending') return;
            // 修改对象状态 ( promiseState )
            self.PromiseState = 'rejected';
            // 设置对象结果值 ( promiseResult )
            self.PromiseResult = data;
            // 调用成功的回调函数 -> 异步
            setTimeout(() => {
                self.callbacks.forEach(item => {
                    item.onRejected(data);
                });
            });
        }
        try {
            // 同步调用『执行器函数』
            executor(resolve, reject);
        } catch(e) {
            // 修改 promise 对象状态为「失败」
            reject();
        }
    }

    // then 方法封装 实现 then 方法的回调
    then(onResolved, onRejected){
        const self = this;
        // 判断回调函数参数
        if(typeof onRejected !== 'function'){
            onRejected = reason => {
                throw reason;
            }
        }
        if(typeof onResolved !== 'function'){
            // value => { return value }
            onResolved = value => value;
        }
        // 返回封装
        return new Promise((resolve, reject) => {
            // 封装函数
            function callback(type){
                try {
                    // 获取回调函数的执行结果
                    let result = type(self.PromiseResult);
                    // 判断
                    if(result instanceof Promise){
                        // 如果是 promise 类型的对象
                        result.then(v => {
                            resolve(v);
                        }, r => {
                            reject(r);
                        })
                    }else{
                        // 结果的对象状态为成功
                        resolve(result);
                    }
                } catch (e) {
                    reject(e);
                }
            }
            // 调用回调函数 PromiseState
            if(this.PromiseState === 'fulfilled'){
                setTimeout(() => {
                    callback(onResolved);
                });
            }
            if(this.PromiseState === 'rejected'){
                setTimeout(() => {
                    callback(onRejected);
                });
            }
            // 判断 pending 状态
            if(this.PromiseState === 'pending'){
                // 保存回调函数
                this.callbacks.push({
                    onResolved: function(){
                        callback(onResolved);
                    },
                    onRejected: function(){
                        callback(onRejected);
                    }
                });
            }
        })
    }

    // catch 方法封装 并实现 then 链中的异常穿透
    catch(onRejected){
        return this.then(undefined, onRejected);
    }

    // resolve 方法封装 => 属于 promise 对象的方法,不是实例的属性
    static resolve(value){
        //返回 promise 对象
        return new Promise((resolve, reject) => {
            if(value instanceof Promise){
                value.then(v => {
                    resolve(v);
                }, r => {
                    reject(r);
                })
            }else{
                // 状态设置为成功
                resolve(value);
            }
        });
    }

    // reject 方法封装 => 属于 promise 对象的方法
    static reject(reason){
        return new Promise((resolve, reject) => {
            reject(reason);
        });
    }

    // all 方法封装
    static all(promises){
        //返回结果为 promise 对象
        return new Promise((resolve, reject) => {
            // 声明计数变量
            let count = 0;
            let arr = []; // 存放成功结果
            // 遍历
            for(let i = 0; i < promises.length; i++){
                promises[i].then(v => {
                    // 得知对象的状态是成功的
                    // 每个 promise 对象都成功再执行 resolve
                    count++;
                    // 将当前 promise 对象成功的结果存入数组
                    // arr.push(v) -> 会存在异步顺序错误问题
                    arr[i] = v;
                    // 判断
                    if(count === promises.length){
                        // 全部成功后调用 resolve 函数
                        resolve(arr);
                    }
                }, r => {
                    rejected(r);
                })
            }
        });
    }

    // race 方法封装
    static race(promises){
        return new Promise((resolve, reject) => {
            for(let i = 0; i < promises.length; i++) {
                promises[i].then(v => {
                    // 修改返回对象的状态为「成功」
                    resolve(v);
                }, r => {
                    // 修改返回对象的状态为「失败」
                    reject(r);
                });
            }
        });
    }
}
5.9.1 mdn文档
5.9.2 async函数
  1. ES7 标准语法;
  2. 返回值为 promise 对象;
  3. promise 对象的结果由函数 async 执行的返回值决定;
// then方法的返回结果一样
async function main(){
	// 1. 如果返回值是一个非promise类型的数据 => 成功
	// return 521;
	// 2.如果返回的是一个promise对象
	// return new Promise((resolve, reject) => {
	// resolve('OK');
	// // reject('ERROR');
	// });
	// 3.抛出异常 => 失败的promise对象
	throw 'oh no';
}
let result = main();
console.log(result);
5.9.3 await 表达式
  1. await 右侧的表达式一般为 promise 对象,但也可以是其他值;
  2. 如果表达式是 promise 对象,则 await 返回的是 promise 成功的值;
  3. 如果表达式是其他值,则将此值作为 await 的值进行返回;
async function main(){
	let p = new Promise((resolve, reject) => {
		resolve('OK');
	});
		let p2 = new Promise((resolve, reject) => {
		reject('ERROR');
	});
	// 1.右侧为promise的情况
	let res = await p; // 返回OK
	// 2.右侧为其它类型的数据
	let res2 = await 20; // 返回20
	// 3.如果promise是失败的状态
	try{
		let res3 = await p2; // 抛出错误
	}catch(e){
		console.log(e);
	}
}
main();
5.9.4 注意事项
  1. await 必须写在 async 函数中,但 async 函数中可以没有 await;
  2. 如果 await 的 promise 失败了,就会抛出异常,需要通过 try catch 来捕获异常;
/* 目标:
 * 读取resource/1.html + 2.html + 3.html
 * 中的内容并完成拼接后输出;
*/
const fs = require('fs');
const util = require('util');
const mineReadFile = util.promisify(fs.readFile);

//使用回调函数方式取html文件
fs.readFile('./resouce/1.html', (err, data1) => {
    if(err) throw err;
    fs.readFile('./resouce/2.html', (err, data2) => {
        if(err) throw err;
        fs.readFile('./resouce/3.html', (err, data3) => {
            if(err) throw err;
            console.log(data1 + data2 + data3);
        });
    });
});
//使用async函数
async function main(){
    try{
        //读取第一个文件的内容
        let data1 = await mineReadFile('./resource/1.html');
        let data1 = await mineReadFile('./resource/2.html');
        let data1 = await mineReadFile('./resource/3.html');
    }catch (e){
        console.log(e);
    }

    console.log(data1 + data2 + data3);
}
main();
2

评论区