Promise入门

Promise入门

(一)Promise的理解和简单使用

一、Promise是什么?

1、理解
(1)抽象表达

Promise是一门新的技术(ES6规范);是js中异步编程的新解决方案(旧方案中是单纯使用回调函数)

异步编程:

1.fs 文件操作
require(‘fs’).readFile(’./index.html’, (err, data)=>{})
2.数据库操作
3.AJAX
$.get(’/server’, (data)=>{})
4.定时器
setTimeout(()=>{})

(2)具体表达
  • 从语法上说:Promise是一个构造函数
  • 从功能上说:promise对象用来封装一个异步操作并可以获取其成功/失败的结果值。
2、为什么要用Promise
(1)指定回调函数的方式更加灵活
  1. 旧方法:必须在启动异步任务前指定回调函数
  2. promise:启动异步任务=>返回promise对象=>给promise对象绑定回调函数(甚至可以在异步任务结束后指定[多个]回调函数)
(2)支持链式调用,可以解决回调地狱问题
  1. 回调地狱?

    回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件

  2. 回调地狱缺点?

    不便于阅读(很多缩进)

    不便于异常处理

  3. 解决方案?

    Promise链式调用

二、Promise对象属性

1、状态属性

promise状态其实是Promise实例对象的一个属性 PromiseState

  • pending 未决定的
  • resolved / fulfilled 成功
  • rejected 失败
2、状态改变
  • pending -> resolved
  • pending -> rejected

只有这两种状态改变,且每个promise对象只能改变一次;无论变为成功还是失败,都会有一个结果数据。

  • 成功的结果数据一般称为value
  • 失败的结果数据一般称为reason
3、结果值

实例对象的另一个属性 PromiseResult,保存的是异步任务对象【成功 / 失败】的结果。

可以通过resolvereject两个函数对值进行修改,然后在then方法进行调用是,传给value/reason

三、Promise基本使用

1、Promise初体验
(1)抽奖系统(定时器)

使用普通回调函数实现功能:

// 生成随机数
        function rand(m, n) {
            return Math.ceil(Math.random() * (n - m + 1) + m - 1);
        }
        /*
            点击按钮,1s后显示是否中奖(30%概率中奖)
        */
        const btn = document.querySelector(".btn");
        btn.addEventListener("click", function () {
            // 定时器
            setTimeout(() => {
                // 30% 
                // 随机获取一个1-100的数字
                let n = rand(1, 100);
                // 判断如果在1-30之间则中奖
                if (n <= 30) {
                    alert("恭喜你中奖了!");
                } else {
                    alert("再接再厉");
                }
            }, 1000);

使用promise实现:

  • Promise构造函数调用时,有两个函数作为参数:

    resolve 解决 => 将promise对象的状态设置为成功

    reject 拒绝 => 将promise对象的状态设置为失败

  • promise对象会调用then方法,里面同样也接收两个函数作为参数:分别是对象成功/失败时调用的函数

			// 使用promise实现
            // resolve 解决 函数类型的数据
            // reject 拒绝 函数类型的数据
            const p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    // 30% 
                    // 随机获取一个1-100的数字
                    let n = rand(1, 100);
                    // 判断如果在1-30之间则中奖
                    if (n <= 30) {
                        resolve();  //将promise对象的状态设置为 成功
                    } else {
                        reject();  //将promise对象状态设置为 失败
                    }
                }, 1000);
            });
            // 调用then方法,同样接收两个函数作为参数;分别代表对象成功时调用的函数和失败时调用的函数
            p.then(() => {
                alert("恭喜你中奖了!");
            }, () => {
                alert("再接再厉");
            });

同时,resolve和reject函数可以返回结果值供then中函数作为参数使用:

这里将n作为结果封装在resolve和reject中,并分别传递给value和reason

		const p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    // 30% 
                    // 随机获取一个1-100的数字
                    let n = rand(1, 100);
                    // 判断如果在1-30之间则中奖
                    if (n <= 30) {
                        resolve(n);  //将promise对象的状态设置为 成功
                    } else {
                        reject(n);  //将promise对象状态设置为 失败
                    }
                }, 1000);
            });
            // 调用then方法,同样接收两个函数作为参数;分别代表对象成功时调用的函数和失败时调用的函数
            p.then((value) => {
                alert("恭喜你中奖了!您的中奖数字为" + value);
            }, (reason) => {
                alert("再接再厉 您的号码为" + reason);
            });

(2)文件操作

普通回调函数实现:

const fs = require('fs');

//回调函数的形式
fs.readFile('Promise/resource/content.txt', (err, data) => {
    if (err) throw err;
    console.log(data.toString());
});

使用Promise形式封装:

const fs = require('fs');

// 使用Promise形式进行封装
let p = new Promise((resolve, reject) => {
    fs.readFile('Promise/resource/content.txt', (err, data) => {
        // 如果出错
        if (err) reject(err);
        // 成功
        resolve(data);
    });
});

// 调用then
p.then(value => {
    console.log(value.toString());
}, reason => {
    console.log(reason);
})
(3)AJAX请求

普通方法实现ajax请求:

// 接口地址:https://api.apiopen.top/getJoke
        const btn = document.querySelector('.btn');
        btn.addEventListener('click', function () {
            const xhr = new XMLHttpRequest();
            xhr.open('GET', "https://api.apiopen.top/getJoke");
            xhr.send();
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status >= 200 && xhr.status < 300) {
                        console.log(xhr.response);
                    } else {
                        console.log(xhr.status);
                    }

                }
            }
        })

使用Pomise对以上ajax请求进行封装:

// 接口地址:https://api.apiopen.top/getJoke
        const btn = document.querySelector('.btn');
        btn.addEventListener('click', function () {
            const p = new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest();
                xhr.open('GET', "https://api.apiopen.top/getJoke");
                xhr.send();
                xhr.onreadystatechange = function () {
                    if (xhr.readyState === 4) {
                        if (xhr.status >= 200 && xhr.status < 300) {
                            resolve(xhr.response);
                        } else {
                            reject(xhr.status);
                        }

                    }
                }
            });
            p.then(value => {
                console.log(value);
            }, reason => {
                console.warn(reason);
            });
        })
2、Promise封装练习
(1)fs模块
/**
 * 封装一个函数 mineReadFile 读取文件内容
 * 参数:path 文件路径
 * 返回值:promise对象
 */

function mineReadFile(path) {
    return new Promise((resolve, reject) => {
        require('fs').readFile(path, (err, data) => {
            if (err) {
                reject(err);
            }
            resolve(data);
        })
    });
}

需要使用时,直接调用即可。

mineReadFile('Promise/resource/content.txt')
    .then(value => {
        console.log(value.toString());
    }, reason => {
        console.log(reason);
    })
(2)util.promisify方法

该方法会返回一个promise对象:

/**
 * util.promisify方法
 */
// 引入util模块
const util = require('util');
// 引入fs模块
const fs = require('fs');
// 返回一个新的函数
let mineReadFile = util.promisify(fs.readFile);

mineReadFile('Promise/resource/content.txt').then(value => {
    console.log(value.toString());
})
(3)ajax

封装ajax:

		/**
         * 封装一个函数sendAJAX 发送GET AJAX请求
         * 参数:URL
         * 返回值:Promise对象
         */
        function sendAJAX(url) {
            return new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest();
                xhr.responseType = 'json';
                xhr.open('GET', url);
                xhr.send();
                xhr.onreadystatechange = function () {
                    if (xhr.readyState === 4) {
                        if (xhr.status >= 200 && xhr.status < 300) resolve(xhr.response);
                        else {
                            reject(xhr.status);
                        }
                    }
                }
            })
        }

调用:

		sendAJAX('https://api.apiopen.top/getJoke').then(value => {
            console.log(value);
        }, reason => {
            console.warn(reason);
        });

四、Promise基本流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-miedzRC8-1634024002900)(Promise入门.assets/image-20211010163606093.png)]

(二)如何使用Promise?

一、API

1、Promise构造函数:Pomise(excutor){}

实例化对象时,需要一个参数,这个参数是一个函数执行器excutor{}

  • excutor函数:执行器 (resolve, reject)=>{}
  • resolve函数:内部定义成功时调用的函数 value => {}
  • reject函数:内部定义失败时调用的函数 reason => {}

excutor会在Promise内部立即同步调用,异步操作在执行器中执行。

比如,以下代码:

const p = new Promise((resolve, reject) => {
    console.log(111);
});
console.log(222);

//output:
111
222
2、Promise.prototype.then方法:(onResolved, onRejected) => {}
  • onResolved函数:成功的回调函数 (value) => {}
  • onRejected函数:失败的回调函数 (reason) => {}

指定用于得到成功value的成功回调和用于失败reason的失败回调。返回一个新的promise对象

3、Promise.prototype.catch方法:(onRejected) => {}

也就相当于失败的回调函数:

p.catch(reason => {
    console.log(reason);
});
4、Promise.resolve方法:(value) => {}

这个方法比较特殊,并不是属于实例对象的,而是属于Promise这个函数对象

接收一个参数value,返回一个成功/失败的promise对象。

  • 如果传入的参数是一个非Promise类型的对象,则返回结果为成功promise对象:
let p1 = Promise.resolve(521);
console.log(p1);  
//[[Prototype]]: Promise
//[[PromiseState]]: "fulfilled"
//[[PromiseResult]]: 521
  • 如果传入的参数是一个Promise对象,则参数的结果决定了resolve的结果:
// 里面的参数是成功的,所以p2也是成功的
let p2 = Promise.resolve(new Promise((resolve, reject) => {
    resolve('OK');
}))
console.log(p2);
//[[Prototype]]: Promise
//[[PromiseState]]: "fulfilled"
//[[PromiseResult]]: "OK"


// 里面的参数是失败的,所以p2也是失败的
let p2 = Promise.resolve(new Promise((resolve, reject) => {
    reject('Error');
}))
console.log(p2);

//[[Prototype]]: Promise
//[[PromiseState]]: "rejected"
//[[PromiseResult]]: "Error"
5、Promise.reject方法

同样也是Promise函数对象的方法,而不是实例对象的方法。

不论传入什么参数,结果都是rejected,就算传入的参数是一个成功的promise对象。

let p = Promise.reject(new Promise((resolve, reject) => {
    resolve('resolve');
}));
console.log(p); 

//[[Prototype]]: Promise
//[[PromiseState]]: "rejected"
//[[PromiseResult]]: "Promise"
6、Promise.all方法:(promises) => {}

promises是包含n个promise的数组,返回一个新的promise,只要所有的promise都成功才算成功,只要有一个失败了就直接失败。

promise对象全都成功:

		let p1 = new Promise((resolve, reject) => {
            resolve('OK');
        })
        let p2 = Promise.resolve('Success');
        let p3 = Promise.resolve('Yeah');

        const result = Promise.all([p1, p2, p3]);
        console.log(result);

/*
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Array(3)
0: "OK"
1: "Success"
2: "Yeah"
*/

如果有失败的promise对象:

		let p1 = new Promise((resolve, reject) => {
            resolve('OK');
        })
        let p2 = Promise.reject('Error');
        let p3 = Promise.resolve('Yeah');

        const result = Promise.all([p1, p2, p3]);
        console.log(result);

/*
Promise {<pending>}
[[Prototype]]: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: "Error"
09-PromiseAPI-all.html:1 Uncaught (in promise) Error
*/
7、Promise.race方法

基本与all方法一致,但race方法是第一个完成的promise的结果状态就是最终的结果状态。

二、Promise的几个关键问题

1、如何改变Promise对象状态
(1)resolve函数
// 1.调用resolve函数
resolve('OK');  // pendding->resolved/fulfilled

// Promise {<fulfilled>: 'OK'}
(2)reject函数
// 2.调用reject函数
reject('Error');  // pendding->rejected

//Promise {<rejected>: 'Error'}
//10-状态修改.html:18 Uncaught (in promise) Error
(3)抛出错误
// 3.抛出错误
throw '出问题了';

//Promise {<rejected>: '出问题了'}
//10-状态修改.html:13 Uncaught (in promise) 出问题了

这里的报错都是因为没有指定失败回调导致的。

2、一个promise指定多个成功/失败回调函数,都会调用吗

当发生状态改变时,指定的回调函数都会被调用:

		let p = new Promise((resolve, reject) => {
            resolve('OK');
        });
        // 指定回调
        p.then(value => {
            console.log(value);
        })
        // 再次指定回调
        p.then(value => {
            alert(value);
        })

以上代码中,因为有resolve('OK'),状态由pending->resolved,所以下面的两个回调函数都会被执行。

但如果把resolve注释掉,也就是状态没有发生改变,那么指定的回调函数就不会被执行。

3、改变promise状态和指定回调函数谁先执行

(1)都有可能,正常情况下是先指定函数再改变状态,但也可以先改变状态再指定回调函数。

(2)先指定回调再改变状态

		let p = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('OK');
            }, 1000);

        });
        p.then(value => {
            console.log(value);
        }, reason => { });

比如在执行器中,改变状态时使用一个定时器。

(3)如何先改变状态再指定回调

  • 执行器中直接调用resolve()/reject()
  • 延迟更长时间才调用then()

(4)什么时候才能得到数据(也就是说回调函数什么时候执行)

  • 如果先指定回调,那当状态发生改变时,回调函数就会调用,得到数据
  • 如果先改变状态,那当指定回调时,回调函数就会调用,得到数据
4、promise.then()返回的新promise结果状态由什么决定
(1)简单表达

由then()指定的回调函数执行结果决定

(2)详细表达
  • 如果抛出异常,新promise变为rejected,且reason为抛出的异常
  • 如果返回的是非promise的任意值,新promise变为resolved,且value为返回的值
  • 如果返回的是另一个新的promise,此promise的结果就会成为新promise的结果
5、promise如何串联多个操作任务

(1)promise的then()返回一个新的promise,可以看成then() 的链式调用

(2)通过then的链式调用串联多个同步/异步任务

		let p = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('OK');
            }, 1000);
        });
        p.then(value => {
            return new Promise((resovle, reject) => {
                resovle("success");
            });
        }).then(value => {
            console.log(value);  //1s后输出success
        }).then(value => {
            console.log(value);  //success后输出undefined
        })
6、异常穿透

(1)当使用promise的then链式调用时,可以在最后指定失败的回调

(2)前面任何操作出了异常,都会传到最后失败的回调中处理

		let p = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('OK');
                // reject('error');
            }, 1000);
        });

        p.then(value => {
            console.log(111);
            throw '出错啦';
        }).then(value => {
            console.log(222);
        }).then(value => {
            console.log(333);
        }).catch(reason => {
            console.warn(reason);
        })
7、中断promise链

(1)当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数

(2)办法:在回调函数中返回一个pendding状态的promise对象【因为状态没有改变,所以后面的then方法都不能执行】

		let p = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('OK');
            }, 1000);
        });

        p.then(value => {
            console.log(111);
            // 中断promise链--返回一个pendding状态的promise对象
            return new Promise(() => { });
        }).then(value => {
            console.log(222);
        }).then(value => {
            console.log(333);
        }).catch(reason => {
            console.warn(reason);
        })

在回调函数中返回了一个pendding状态的promise对象,就会导致没有状态改变,也就是后面的then方法都不能被调用,从而实现中断promise链。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值