Promise

93 阅读6分钟

Promise

一. promise的基本语法:

let p1 = new Promise((resolve, reject) => {

// resolve和reject是Promise内提供的2个函数, 用于回调返回结果到外面

resolve('111') // 触发.then()小括号里函数体执行

reject('222') // 触发.catch()小括号里函数体执行

}).then(res=>{
 console.log(res)
 // res = 111
}).catch(err=>{
console.log(err)
 // err = 111
})

二. promise的状态和值:

promise的状态

pending: 准备; resolved(或者fulfilled) 成功; rejected: 失败

准备:new实例化后, Promise对象(pending准备状态)

成功:当Promise内代码执行了resolve, 会导致所在的Promise对象 变成 fulfilled成功状态

失败:当Promise内代码执行了reject, 会导致所在的Promise对象 变成 rejected失败状态

promise的值

resolve(值),这个值会自动传递给then

reject(值),这个值会自动传递给catch

三.then的格式及执行逻辑

then方法的作用是为Promise对象添加状态改变时的回调函数。

下面从其调用格式,执行逻辑及返回值三个方面来介绍

1.then的调用格式

它有两个参数,每个参数都是函数。 第二个参数是可选的。

// p 是一个promise对象
p.then(函数1[,函数2])

它的两个参数都是函数。

  • 第一个参数是resolved状态的回调函数。当p的状态从pending变成了resolved,函数1会执行。
  • 第二个参数是rejected状态的回调函数。 当p的状态从pending变成了rejected,函数2会执行。

其中第二个参数是可选的,如果只写一个参数的话就是如下:

promise对象.then(函数1)

2.执行逻辑

以如下代码为例

let p = new Promise((resolve,reject)=>{
                    //  resolve(val1);
    reject(val2)
})

p.then(
    okVal =>{
        console.info("成功");
    	console.log(okValue);
    }, 
	errVal=>{
    	console.info("失败");
    	console.log(errValue);
    }
)

它的两个参数都是函数,其执行逻辑是:

  • 如果promise对象的状态是resolved,则then()会执行第一个函数,并传入当前的PromiseValue(即上面的val1);
  • 如果promise对象的状态是rejected,则then()会执行第二个函数,并传入当前的PromiseValue(即上面的val2);
  • 特别地,如果promise对象的状态是rejected,且此时then方法并没有设置第二个参数,就会向外抛出一个错误,错误的提示大约是Uncaught (in promise)。

如下是图示

Snipaste_2022-11-17_22-07-47.png

3.then的返回值(难点)

then()方法的返回值也是一个promise对象,但是要注意的是它的返回值是一个新的promise对象,与调用then方法的并不是同一个对象

看如下代码:

let p1 = new Promise(()=>{});
let p2 = p1.then(function f_ok(){}, function f_err(){}); 
// p2也是一个promise对象。

console.log(p1 === p2); // false

如上代码可以说明p1.then()的结果是一个与p1不同的promise对象。换句话说,then()会封装一个全新的promise对象p2。那既然 p2也是一个promise对象,那么,p2的状态(promiseStatus)和值(promiseValue)分别是什么?

p2的状态及promiseValue如何确定?

p2的状态及promiseValue按如下规则来确定

  • 如果p1的状态是pending,则p2的状态也是pending。
  • 如果p1的状态是resolved,then()会去执行f_ok,则p2的状态由f_ok的返回值决定。
    • 如果f_ok返回值不是promise对象,则p2的状态是resolved,且p2的promiseValue就是f_ok函数的return值。
    • 如果f_ok返回值是一个promise对象,则p2的状态及promiseValue以这个promise对象为准。
    • 如果f_ok这个函数内部发生了错误(或者是用户主动抛出错误),则p2的状态是rejected,且p2的promiseValue就是这个错误对象。
  • 如果p1的状态是rejected,then()会去执行f_err,则p2的状态由f_err的返回值决定。
    • 如果f_err返回值不是promise对象,则p2的状态是resolved,且p2的promiseValue就是f_err函数的return值。
    • 如果f_err返回值是一个promise对象,则p2的状态及promiseValue以这个promise对象为准。
    • 如果f_err这个函数内部发生了错误(或者是用户主动抛出错误),则p2的状态是rejected,且p2的promiseValue就是这个错误对象。

四.catch的格式及用法

catch 是 then(null, reject)的别名

catch()的格式及用法

Promise.prototype.catch 是 Promise.prototype.then(null, reject)的别名,用于指定当promise对象的状态从pending变成rejected的回调函数 。

let p1 = new Promise((resolve,reject)=>{
  reject('s')
});

p1.catch(function(err){
  console.log(err);
})

// 与下面的代码等价
p1.then(null, function(err){
  console.log(err);
})

单独使用catch没有什么意义,它一般与then一块使用。如下:

new Promise((resolve,reject)=>{

}).then(function(result){
  // 如果promise对象的状态是resolved的,就会到这里来,并且result会接收promiseValue的值
}).catch(function(err){
  // 如果promise对象的状态是rejected的,就会到这里来,并且err会接收promiseValue的值
})

// 上面的代码如何拆分来写的话,等价于:
let p1 = new Promise((resolve,reject){

});
let p2 = p1.then(function(result){

});
let p3 = p2.catch(function(err){

})

catch的返回值

catch的返回值仍是一个promise对象,确定它的值的方式与then(null,(errVal)=>{ })的方式一致。

五.Promise-链式调用

p.then()的返回值是一个新的promsie对象

编写代码进行讲解

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功1')
    }, 2000)
})

let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功2')
    }, 2000)
})

p.then(res => {
    console.log(res);
    return p2 // 如果return一个Promise对象,它的结果, 会被下个then接收
}).then(res => {
    console.log(res);
})

六.Promise-解决回调地狱

// 前提: axios函数在原地返回的就是一个Promise对象
let pname = ''
axios.get('http://ajax-api.itheima.net/api/province').then(res => {
    // 2. 获取某个省, 对应的城市列表
    pname = res.data.data[5];
    return axios.get(`http://ajax-api.itheima.net/api/city?pname=${pname}`)
}).then(res => {
    // 3. 获取某个市, 对应的地区列表
    let cname = res.data.data[0]
    return axios.get(`http://ajax-api.itheima.net/api/area?pname=${pname}&cname=${cname}`)
}).then(res => {
    console.log(res);
})

七.Promise的静态方法

Promise.all()方法

// 静态(类)方法: 直接用Promise类来调用
// 1. Promise.all() 
// 作用: 合并多个Promise对象, 等待所有成功后, 返回结果
// 语法: Promise.all([promise对象1, promise对象2, ...]).then()
// 特点: 返回最终结果是个数组, 值是按顺序对应小Promise对象的成功结果
// 注意: 如果有1个Promise失败, 则整个Promise对象则失败
let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功1')
    }, 2000)
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功2')
    }, 2000)
})
let p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功3')
    }, 2000)
})

Promise.all([p1, p2, p3]).then(res => {
    console.log(res);
})

Promise.allSettled

// Promise.allSettled的语法和Promise.all一致   但返回值有区别  Promise.allSettled([p1, p2, p3])会返回状态和值而Promise.all值返回值且失败时只返回失败的值
	// 作用: 合并多个Promise对象, 等待所有成功后, 返回结果
	// 语法: Promise.all([promise对象1, promise对象2, ...]).then()
	// 特点: 返回最终结果是个数组, 值是按顺序对应小Promise对象的成功结果
	// 注意: 如果有1个Promise失败, 则整个Promise对象则失败

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

Promise.race()方法

// 静态(类)方法: 直接用Promise类来调用
// 1. Promise.race() - 赛跑机制
// 作用: 发起并行多个Promise对象, 等待只要任何一个有结果(成功|失败), 返回结果执行then
// 语法: Promise.race([promise对象1, promise对象2, ...]).then()
// 特点: 返回第一个有结果的promise对象的结果
let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功1')
    }, 2000)
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功2')
    }, 2000)
})
let p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功3')
    }, 2000)
})

Promise.race([p1, p2, p3]).then(res => {
    console.log(res);
})

Promise的静态方法resolve和reject

Promise.resolve

创建一个状态为resolved的promise对象,并指定其值。或者理解为:把值转成Promise对象,并设置状态和值。 格式如下:

const p = Promise.resolve(值)

Promise.reject

格式如下:

const p = Promise.reject(值)