Promise 从基础到深入

  1. promise是什么
  • promise是ES6中的特性,是异步编程的一种解决方案。
  • promise的作用就是对异步请求的封装。
  • promise是一个对象,对象和函数的区别就是对象可以保存状态,函数不可以(闭包除外)
  • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
  1. 为什么要用promise
  • [1]. 在异步请求中,指定回调函数的方式更加灵活
    • 旧方式中,回调函数必须在启动异步任务前指定
    • promist: 启动异步任务》返回promise对象》给promise对象绑定回调函数(甚至可以在异步任务结束后指定)
  • [2]. 支持链式调用,可以解决回调地狱问题
    • 解决方案:promise链式调用(不好在还有回调函数)
    • 终极解决方案:async/await
  1. Promise总结
  • [1]. Promise的状态一经改变就不能再改变。
  • [2]. .then和.catch都会返回一个新的Promise。
  • [3]. .catch不管被连接到哪里,都能捕获上层未捕捉过的错误。
  • [4]. 在Promise中,返回任意一个非 promise 的值都会被包裹成 promise 对象,例如return 2会被包装为return Promise.resolve(2)。
  • [5]. Promise 的 .then 或者 .catch 可以被调用多次, 但如果Promise内部的状态一经改变,并且有了一个值,那么后续每次调用.then或者.catch的时候都会直接拿到该值。
  • [6]. .then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获。
  • [7]. .then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环
  • [8]. .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透
  • [9]. .then方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候你可以认为catch是.then第二个参数的简便写法。
  • [10]. .finally方法也是返回一个Promise,他在Promise结束的时候,无论结果为resolved还是rejected,都会执行里面的回调函数。且它的回调函数是接收不到Promise的结果的
  • [11]. .finally的返回值如果在没有抛出错误的情况下默认会是上一个Promise的返回值
  1. Promise.all()和Promise.rece()总结
  • [1]. Promise.all()的作用是接收一组异步任务,然后并行执行异步任务,并且在所有异步操作执行完后才执行回调。
  • [2]. .race()的作用也是接收一组异步任务,然后并行执行异步任务,只保留取第一个执行完成的异步操作的结果,其他的方法仍在执行,不过执行结果会被抛弃。
  • [3]. Promise.all().then()结果中数组的顺序和Promise.all()接收到的数组顺序一致。
  • [4]. all和race传入的数组中如果有会抛出异常的异步任务,那么只有最先抛出的错误会被捕获,并且是被then的第二个参数或者后面的catch捕获;但并不会影响数组中其它的异步任务的执行。

(一) promise 基础

new Promise((resolve, reject)=>{
	//一段耗时的异步操作
	//判断操作是否成功
	if(true){
		resolve('成功');//操作成功,并传递变量
	}else{
		reject('失败');//操作失败,并传递变量
	}
}).then(
	(success)=>{console.log(success);}, //操作成功时执行该方法,onResolved
	(error)=>{console.log(error);}	//操作失败时执行该方法,onRejected
);
  • Proimise的回调函数是同步函数
  • resolve作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
  • reject作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
  • promise有三个状态:
    1、pending[待定]初始状态
    2、fulfilled[实现]操作成功
    3、rejected[被否决]操作失败
    当promise状态发生改变,就会触发then()里的响应函数处理后续步骤;
    promise状态一经改变,不会再变。
    Promise对象的状态改变,只有两种可能:
    从pending变为fulfilled
    从pending变为rejected。
    这两种情况只要发生,状态就凝固了,不会再变了。在这里插入图片描述

(二) promise 嵌套

new Promise(
   (resolve, reject)=>{
        setTimeout(()=>{
            resolve('这是第一层');
        }, 1000);
    }
).then((res)=>{
    console.log(res);
    new Promise(
        (resolve, reject)=>{
            setTimeout(()=>{
                reject("第二层");
            }, 1000);
        }
    ).then((res)=>{
        console.log(res);   
    }).catch((res)=>{
        console.log(res + "失败");               
    });
}).catch((res)=>{
    console.log("第一层失败");
});
  • 当操作执行失败的时候会进入到catch()方法中,当然也可以在then中进行错误的处理
    第一种:reject(‘错误信息’).then(() => {}, () => {错误处理逻辑})
    第二种:throw new Error(‘错误信息’).catch( () => {错误处理逻辑})
    推荐使用第二种方式,更加清晰好读,并且可以捕获前面所有的错误(可以捕获N个then回调错误)

(三) promise 链式调用

  • .then()和.catch()方法可以返回一个新的Promise实例,所以可以链式调用

  • 如果你在 then 中 使用了 return,那么 return 的值会被 Promise.resolve() 包装

    new Promise(resolve => {
        console.log('step 0');
        setTimeout(() => {
            resolve('100');
        }, 1000);
    }).then(value => {  //value = 100
        //将这个新的promise传递下去
        return new Promise(resolve => {
            console.log('step-1-1');
            setTimeout(()=>{
                resolve('110');
            }, 1000);                
        }).then(value => {  //value = 110  关键点在这里
            console.log('step-1-2');
            return value;   //value = 110;// 包装成 Promise.resolve(110)
        }).then(value => {
            console.log("step-1-3");
            return value;   //value = 110               
        });
    }).then(value => {  
        console.log(value); //value = 110
         return new Promise(resolve =>{
            console.log('step 2'); 
            resolve('120');
        }); 
    }).then(value => {
        console.log("step 2-1");
        console.log(value); //vaule = 120
    });
    
  • 简化版

     new Promise((resolve, reject) => {
         setTimeout(()=>{
              resolve("hello World");
          }, 1000);
      }).then(data => {
          console.log(data + '11'); //输出:hello world11
          return data;
      }).then(data => {
          console.log(data);//输出:hello world
          return data + '222';
      }).then(data => {
          console.log(data);//输出:hello world222
      });
    

(四) promise.all()

  • Promise.all([p1, p2, p3])用于将多个promise实例,包装成一个新的Promise实例,返回的实例就是普通的promise
  • Promise.all接收一个数组作为参数
    数组里可以是Promise对象,也可以是别的值,只有Promise会等待状态改变
    当所有的子Promise都完成,该Promise完成,返回值是全部值得数组
    有任何一个失败,该Promise失败,返回值是第一个失败的子Promise结果
     // 1.方法1
    function fun1(){
       var p = new Promise((resolve, reject)=>{
            setTimeout(()=>{
                resolve('fun1');
            },1000);
        }); 
        return p;
    }
    
    // 2. 方法2
    function fun2(){
        var p = new Promise((resolve, reject)=>{
            setTimeout(()=>{
                resolve({name: 'fun2', arg: 'abc'});
                //reject({name: 'fun2', arg: 'abc'});
            },2000);
        }); 
        return p;
    }
    Promise.all([fun1(), fun2()]).then((res)=>{
        console.log(res);
        console.log('两个方法都成功');
    }).catch(res => {
        console.log('失败');
        console.log(res);
        
    });
    

(五) promise.race()

Promise.race([p1, p2, p3]),当p1,p2,p3中谁最先返回 Promise实例就以谁为准,比如p1最先返回并返回失败,则整体进入catch()方法中,p1最先返回并返回成功,则整体进入then()方法中。

 // 1.方法1
function fun1(){
   var p = new Promise((resolve, reject)=>{
        setTimeout(()=>{
            //resolve('fun1');
            reject('fun1');
        },1000);
    }); 
    return p;
}

// 2. 方法2
function fun2(){
    var p = new Promise((resolve, reject)=>{
        setTimeout(()=>{
            resolve({name: 'fun2', arg: 'abc'});
            // reject({name: 'fun2', arg: 'abc'});
        },2000);
    }); 
    return p;
}
Promise.race([fun1(), fun2()]).then((res)=>{
    console.log(res);
    console.log('成功');
}).catch(res => {
    console.log('失败');
    console.log(res);
});

(六) 关于promise的几个问题

[1]. 主动/系统抛出异常

  • 抛出异常:如果当前是pendding就会变为rejected

  • 一个promise实例指定多个成功/失败回调函数,都会被调用

    const p = new Promise((resole, reject) => {
       //   第一种抛出异常
      //throw new Error("出错了");
      // 第二种抛出,会进入到错误处理函数
      throw 3
    });
    p.then((info) => {
      console.log(info);
    }).catch((err) => {
      console.log('err',err);
    });
    //p状态改变,这里也会被捕获
     p.then((info) => {
       console.log(info);
    }).catch((err) => {
       console.log('err1',err);
    });
    

[2]. 状态改变和指定回调函数的顺序

  • 正常情况下是指定回调函数再改变状态,但也可以先该改变状态在指定回调函数
  • 如何先改变状态再调用回调函数?
    • ① 在执行器中直接调用resolve()/reject()
    • ②延迟更长时间才调用then()
  • 什么时候才能得到数据?
    • ① 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
    • ②如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据
    //先指定回调函数,在改变状态(将计时器去掉就是先改变状态后调用回调函数)
    new Promise((resolve, reject)=>{
      setTimeout(()=>{
            resolve(2)//后改变的状态(同时指定数据),异步执行回调函数
        },2000)
    }).then(value=>{	//先指定回调函数,保存当前指定的回调函数
        console.log(value); 
    });
    

[3]. then()返回新的promise的状态

  • promise.then()返回的新promise的结果状态由什么决定。
    • ①简单表达:由then()指定的回调函数执行的结果决定
    • ②详细表达:
      • 1.如果抛出异常,新的promise变为rejected,reason为抛出的异常
      • 2.如果返回的是非promise的任意值,新promise变为resolved,value为返回的值
      • 3.如果返回的是另一个新promise,此promise的结果就会成为新promise的结果
  • .then() 是立即执行函数,.then()中的回调函数是异步执行函数
    new Promise((resolve, reject) => {
     resolve(1);
    })
      .then(
        (value) => {
          console.log("onResolved1()", value);
          //情况1: 什么都不返回
    
          //情况2: 返回一个非promise
          //return { name: "zs" };
          //情况3:返回一个新的promise并且状态为fulfilled即成功
          //return Promise.resolve(3);
          //情况4:返回一个新的promise并且状态为rejected即失败
          //return Promise.reject(4);
          //情况5:抛出一个异常或任意的数据
          throw 5
        },
        (reason) => {
          console.log("onRejected1()", reason);
        }
      )
      .then(
        (value) => {
          //情况1的结果:undefine,即没有实参传入
          //情况2的结果:{ name: "zs" }
          //情况3的结果:3
          console.log("onResolved2()", value);
        },
        (reason) => {
          //情况四的结果:4
          //情况五的结果:5
          console.log("onRejected2()", reason);
        }
      );
    

[4]. promise串联多个操作任务

  • 由于promise的then()返回的是一个新的promise,可以将then()链式调用

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

  • then()中如果是一个异步任务,则需要返回一个新的promise

    new Promise((resolve, reject) => {
      setTimeout(() => {
      	//如果这里是reject(1)则第一个then执行失败的处理函数,第二个then执行的是成功的处理函数还是失败的处理函数与第一个then的返回结果有关,与第一个promise无关
        resolve(1);   
      }, 1000);
    })
      .then((value) => {
        console.log("任务1的结果", value);
        console.log("执行任务2(同步)");
        return 2;
      })
      .then((value) => {
        console.log("任务2的结果", value);
        //返回一个新的promise,否则还是最开始的promise
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            console.log("执行任务3(异步)");
            //return 3;
            resolve(3);
          }, 2000);
        });
      })
      .then((value) => {
        console.log("任务3的结果", value);
      });
    

[5]. promise异常穿透

当使用promise的then链式调用时,可以在最后指定失败的回调,前面任何操作出了异常,都会传到最后失败的回调中处理。

new Promise((resolve, reject) => {
 reject(1);
})
 //then中没有处理错误的回调函数时,默认处理相当于:resason=>Promise.reject(reason)
 /*
 then(
     (value) => {
         console.log("onResolved1()", value);
         return 2;
     },
     reason => Promise.reject(reason)
 )
 */
 .then((value) => {
   console.log("onResolved1()", value);
   return 2;
 })
 .then((value) => {
   console.log("onResolved2()", value);
   return 3;
 })
 .then((value) => {
   console.log("onResolved3()", value);
   return 4;
 })
 .catch((reason) => {
   console.log("error:", reason);
 });

[6]. 中断promise链

当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数,可以在回调函数中返回一个pendding状态的promise对象

 new Promise((resolve, reject) => {
 reject(1);
 })
   .then((value) => {
     console.log("onResolved1()", value);
     return 2;
   })
   .catch((reason) => {
     console.log("error:", reason);
     //返回一个状态为pending的promise,就不会执行后面的then
     return new Promise(() => {}); 
   })
   .then(
     (value) => {
       console.log("onResolved4()", value);
       return 4;
     },
     (reason) => {
       console.log("onRejected()", reason);
     }
   );

(七) 简单应用

[1]. 使用promise实现休眠效果

async function sleep(ms) { //async 非必须
    return await new Promise(resolve => { //如果不使用 async 则不使用 await 
        setTimeout(() => {
            console.log(ms / 1000 + '秒后');
            resolve(1);
        }, ms);
        // setTimeout(resolve , ms); //简化版
    });
}

async function sleepTest() {
    await sleep(1000);
    console.log(123);
}
sleepTest();
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值