前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JS高阶(一)Promise

JS高阶(一)Promise

作者头像
DioxideCN
发布2022-08-05 19:30:42
2.4K0
发布2022-08-05 19:30:42
举报
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
代码语言:javascript
复制
//返回为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 的值;

基本流程
代码语言:javascript
复制
//创建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) => {}
代码语言:javascript
复制
//创建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 对象;
代码语言:javascript
复制
//创建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 对象;
代码语言:javascript
复制
//创建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 都成功才返回成功,只要有一个失败则返回失败;
代码语言:javascript
复制
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 的结果状态就是最终的结果状态;
代码语言:javascript
复制
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状态;
代码语言:javascript
复制
let p = new Promise((resolve, reject) => {  
 //resolve函数  
 resolve('ok'); // pending -> fulfilled(resolved)  
 reject('error'); // pending -> rejected  
 throw 'sth. wrong'; //抛出错误(需要一个失败回调)  
});
  1. 一个 promise 函数指定多个 成功/失败 回调函数,都会调用吗?
    • 当 promise 改变为对应状态时,都会调用
代码语言:javascript
复制
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();
    • 什么时候才能得到数据?
      • 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据;
      • 如果先改变状态,那当指定回调时,回调函数就会调用,得到数据;
代码语言:javascript
复制
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 的结果;
代码语言:javascript
复制
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() 的链式调用可以串联多个 同步/异步 任务;
代码语言:javascript
复制
//规避回调地狱
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 链式调用时,可以在最后指定失败的回调;
    • 在前部出现的所有异常都会穿透至最后的失败回调中;
代码语言:javascript
复制
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 对象;
代码语言:javascript
复制
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类
代码语言:javascript
复制
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文档
  1. ES7 标准语法;
  2. 返回值为 promise 对象;
  3. promise 对象的结果由函数 async 执行的返回值决定;
代码语言:javascript
复制
// 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 的值进行返回;
代码语言:javascript
复制
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 来捕获异常;
代码语言:javascript
复制
/* 目标:
 * 读取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();
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Promise是什么?
  • 为什么要使用Promise?
  • 5.2.1 对象状态改变『PromiseState』
  • 5.2.2 对象结果值的属性『PromiseResult』
  • 基本流程
  • 使用Promise
    • 5.4.1 API
    • resolve 方法
    • reject 方法
    • all方法
    • race方法
    • Promise内的关键问题
      • 5.8 定义整体结构并封装成class类
        • 5.9.1 mdn文档
          • 5.9.2 async函数
            • 5.9.3 await 表达式
              • 5.9.4 注意事项
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档