Promise深入理解

目录

Promise是什么?

什么是回调地狱 ?

回调函数

 异步任务

回调地狱

 Promise解决回调地狱

Promise对象

prototype

PromiseState

PromiseResult

Promise.prototype属性

then

catch

 finally

Promise.all()

Promise.race()

Promise.resolve()

Promise.reject()

async/await

 async

 await


Promise是什么?

 promise 是es6中新增加对象,产生是为了解决异步回调地狱问题。利用then函数的链式调用解决了异步回调地狱问题。

什么是回调地狱 ?

  • 回调函数

 当一个函数作为参数传入另一个参数中,并且它不会立即执行,只有当满足一定条件后该函数才可以执行,这种函数就称为回调函数。setTimeout就是最常见的回调函数。

setTimeout(function(){   //function(){console.log('执行了回调函数')}就是回调函数,它只有在3秒后才会执行
	console.log('执行了回调函数');
},3000)  //3000毫秒
  •  异步任务

 与之相对应的概念是“同步任务”,同步任务在主线程上排队执行,只有前一个任务执行完毕,才能执行下一个任务。异步任务不进入主线程,而是进入异步队列,前一个任务是否执行完毕不影响下一个任务的执行。同样,还拿定时器作为异步任务举例:

   setTimeout(function(){
            console.log('执行了回调函数');
        },3000)
        console.log('111');

 这种不阻塞后面任务执行的任务就叫做异步任务。
接下来让我们看看什么是回调地狱。

  • 回调地狱

 setTimeout(function () {  //第一层
        console.log('111');
        setTimeout(function () {  //第二程
            console.log('222');
            setTimeout(function () {   //第三层
                console.log('333');
            }, 1000)
        }, 2000)
    }, 3000)

可以看到,代码中的回调函数套回调函数,居然套了3层,这种回调函数中嵌套回调函数的情况就叫做回调地狱。

总结一下,回调地狱就是为是实现代码顺序执行而出现的一种操作,它会造成我们的代码可读性非常差,后期不好维护。

那该如何解决回调地狱呢?

 Promise解决回调地狱

 Promise是js中的一个原生对象,是一种异步编程的解决方案,可以替换掉传统的回调函数解决方案。

  • Promise构造函数接收一个函数作为参数,我们需要处理的异步任务就卸载该函数体内,该函数的两个参数是resolve,reject。异步任务执行成功时调用resolve函数返回结果,反之调用reject。
  • Promise对象的then方法用来接收处理成功时响应的数据,catch方法用来接收处理失败时相应的数据。
  • Promise的链式编程可以保证代码的执行顺序,前提是每一次在than做完处理后,一定要return一个Promise对象,这样才能在下一次then时接收到数据。
console.log(1);

setTimeout(() => {
            console.log(6);
        }, 0);
        let p = new Promise((resolve, reject) => {
            // 1:该函数是不是回调函数? 是
            // 2:该函数是同步执行还是异步执行? 同步执行
            // 3:话说promise实例对象是为了解决异步回调地狱问题?究竟哪里的程序是异步程序呢?then 的回调函数异步执行
            console.log(2);
            // 1 resolve 是触发 then实参一函数的条件之一
            // 2 resolve 同步触发函数,但是 then的回调函数是异步触发的
            //   因为then的回调函数,在Promise内部有多个执行条件,resolve执行只是条件之一
     
            resolve()

            // reject 是触发then 实参二函数的条件之一,也是触发catch的实参函数条件之一。
            // reject()
        })
        console.log(3);
        p.then(() => {
            console.log(4); // 异步执行的
        }, () => {
            console.log('reject 触发');
        })
        console.log(5);

        //执行结果为:1,2,3,5,4,6

        // 为什么setTimeOut 回调执行,比 then 的回调慢?
        // then回调是微任务。setTimeOut是宏任务。在忽略Script情况下先微任务后宏任务。

总结:

  • Peomise的回调函数是同步执行的,只有then的回调的异步的
  • then()方法有两个参数,第一个是resolve成功的回调,第二个是reject失败的回调
  • 当宏任务和微任务同时异步时,在忽略Script的情况下,先执行微任务再执行宏任务

Promise对象

 Promise对象上有三个属性

  • [[prototype]]
  • [[PromiseState]]
  • [[PromiseResult]]

prototype

[[Prototype]] 是Promise的原型,属性为:

constructor:指向构造函数Promise
then:当实例状态变为成功的时候,调用then函数
catch:当实例的状态变为失败的时候,调用catch函数
finally:当实例为未完成状态变为已完成状态的时候调用的方法 

 Promise深入理解_第1张图片

PromiseState

 [[PromiseState]]为Promise的状态,有三种:

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled):意味着操作成功完成。
  • 已拒绝(rejected):意味着操作失败。

当调用Promise对象的resolve()方法时,Promise的状态 [[PromiseState]]由pending变为fulfilled,当调用Promise对象的reject()方法时,Promise的状态 [[PromiseState]]由pending变为rejected

PromiseResult

[[PromiseResult]]  记录Promise的运算结果,初始值为resolve()方法的参数,没有参数则为undefind,只能通过then()方法获取,通过then方法的return进行赋值

获取PromiseResult

let p = new Promise((resolve,reject)=>{
            resolve(123) 
          
        })
      
        p.then(val=>{
            console.log('读取p对象中[[PromiseResul]]的赋值---->',val);
        })

给PromiseResult赋值

  
let p1 =  p.then((res)=>{
           
            return '给p1.[[PromiseResult]]赋值'

        },()=>{
            console.log('reject');
        })
        console.log(p1);

Promise.prototype属性

Promise.prototype有三个api: then catch finally

then

then:因为Promise的then()方法返回的是一个Promise实例,所以可以通过then()进行链式调用

    let p = new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve(123)
            },1000)
        })
        let p1 = p.then(res=>{ // res=? 123
            return res+1
         }).then(res=>{// res=? 124
            return 'ni'+res
        }).then(res=>{ // res=? ni1234
             return 'hao'+res  
        })

catch

catch:1 捕获.then链式调用中的错误

                 2 统一 处理 promis对象 的 已经拒绝的状态

   let p = new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve(123)
            },1000)
        })
        let p1 = p.then(res=>{ // res=? 123
            return res+1
         }).then(res=>{// res=? 124
            return 'ni'+res
        }).then(res=>{ // res=? ni1234
             return 'hao'+res  
        }).catch((err)=>{
           console.log(err);

        })

 finally

finally:用于指定不管 Promise 对象最后状态如何,都会执行的操作

  let p = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(123)
            }, 1000)
        })
        let p1 = p.then(res=>{
            console.log(1);
        }).then(res=>{
            console.log(2);
        }).then(res=>{
            console.log(3);
        }).catch((err)=>{
            console.log(4);
            console.log(err);
        }).finally(()=>{
          
            console.log('finally');
        })
        .then(res=>{
           console.log('验证finally后是否能够继续then');    
        })
        console.log(p1);

Promise.all()

作用:可以同时触发多个promise,用于处理并发任务,等多个异步都结束的在执行下一个任务

参数:元素:元素promise实例对象

返回值:全新的promise实例 [[PromiseResult]]:[p结果,p2结果,p3结果]

then  三个实例对象都变为 已接受状态 时候触发回调函数

回参:数组:数组元素为三个实例对象的[[promiseResult]],话句话三个实例对象的运算结果

let p = new Promise((resolve,reject)=>{
            console.log('第一个异步任务开始了');
            setTimeout(() => {
                resolve('第一次异步结果')
            }, 1000);
        })
        let p2 = new Promise((resolve,reject)=>{
            console.log('第二个异步任务开始了');
            setTimeout(() => {
                resolve('第二次异步结果')
                // reject()
            }, 2000);
        })
        let p3 = new Promise((resolve,reject)=>{
            console.log('第三个异步任务开始');
            setTimeout(() => {
                resolve('第三次异步结果')
            }, 3000);
        })
        let state = new Date().getTime();
       
        Promise.all([p,p2,p3]).then(arr=>{
          
            let timer = (new Date().getTime()) - state;
            console.log('三个异步都结束了');
            console.log(arr);
            console.log(timer);
        },()=>{
            console.log('有一个拒绝的');
        })

Promise.race()

Promise.race()

作用:多个promise实例,只要接收第一个变为 已完成.

参数:元组<promise实例>

返回: 新的promise实例

注意: 同时触发多个promise , 只要有一个状态变为 已完成,返回的promise就是,已完成状态的promise,并且[[PromiseResult]]的值为 该已完成状态 的值

用途:多个异步任务中;可以找到第一个成功的异步任务,一级异步运算结果

 var promise1 = function () {
            return new Promise(function (resolve) {
                setTimeout(function () {
                    console.log(1);
                    resolve(1);
                }, 1000)
            });
        }
        var promise2 = function () {
            return new Promise(function (resolve,reject) {
                setTimeout(function () {
                    console.log(2);
                    resolve(2);
                }, 2000);
            });
        }
      
        Promise.race([promise1(), promise2()])
            .then(function (val) {
                console.log('有⼀个 promise 状态已经改变', val);
            })

all  race 区别

  • 相同点:都是返回promise,作用:捕获异步结果
  • all 多个异步同时触发,最后一个成功,all的状变成功,捕获到 所有异步运算结果[[PromiseResult]]
  • race 多个异步同时触发,第一个成功,race的状态变成功,捕获到 第一个异步运算结果 [[PromiseResult]]

Promise.resolve()

Promise.resolve()

作用:创建一个 已完成状态的promise实例

返回值:promise实例

实参: 作用: 给[[PromiseReuslt]] 赋值

 var promise = Promise.resolve(123);
        console.log(promise);
        promise
            .then(function (val) {
                console.log('已完成', val);
            });

Promise.reject()

Promise.reject()

作用:创建一个已拒绝状态的promise 实例

返回值:promise实例

实参: 赋值错误对象

   var promise = Promise.reject(new Error('已经拒绝'));
        console.log(promise);
        promise
            .then(null,function (val) {
                console.log('已拒绝', val);
            });

async/await

async/await其实是Promise的语法糖,它能实现的效果都能用then链来实现,这也和我们之前提到的一样,它是为优化then链而开发出来的。从字面上来看,async是“异步”的简写,await译为等待,所以我们很好理解async声明function是异步的,await等待某个操作完成。 

  •  async

作为一个关键字放在函数的前面,表示该函数是一个异步函数,意味着该函数的执行不会阻塞后面代码的执行 异步函数的调用跟普通函数一样

async function asy(){
    return "hello";
}
console.log(asy());
// Promise { 'hello' }

 可以看出asy函数的返回结果是一个Promise对象,要获取Promise的返回值应该用then方法

async function asy(){
    return "hello";
}
asy().then((result)=>{
    console.log(result);
});
console.log("world");

// world
// hello

此时先输出的就是后面的字符串’world‘,说明异步函数的执行没有阻塞后面的代码执行,async的内部实现原理就是如果该函数中有一个返回值,当调用该函数时,默认会把return后面直接量通过Promise.resolve()返回Promise对象,若函数内部抛出错误,则调用Promise.reject()返回一个Promise 对象,因为返回的是一个Promise对象,所以可以使用Promise的所有用法,例如:then,catch,finally

  •  await

  •  按照语法说明,await等待的是一个Promise对象,或者是其他值(也就是说可以等待任何值),如果等待的是Promise对象,则返回Promise的处理结果;如果是其他值,则返回该值本身。
  • await会暂停当前async function的执行,等待Promise的处理完成。若Promise正常处理(fulfillded),其将回调的resolve函数参数作为await表达式的值,继续执行async function;若Promise处理异常(rejected),await表达式会把Promise异常原因抛出;另外如果await操作符后面的表达式不是一个Promise对象,则返回该值本身。
function test1(x){
   return new Promise(resolve=>{setTimeout(() => {
       resolve(x);
     }, 3000)
    }
   )
}
async function test2(){    
  let result =  await test1('hello world');
  console.log(result);    // 3秒钟之后出现hello world
}
test2();

 上面可以看出await等待的是一个Promise,并且处理结果为resolve,所以返回的是'hello world'

 function test1(x){
   return new Promise(resolve=>{setTimeout(() => {
       resolve(x);
     }, 3000)
    }
   )
}
async function test2(){    
  let result =  await test1('123');
  console.log(result);    // 3秒钟之后出现123
  console.log('456')   // 3秒钟之后出现456
}
test2();
console.log('789')  //立即输出789

这就是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。await暂停当前async的执行,所以'789''最先输出,'123'和‘456’是3秒钟后同时出现的。

 与Promise对比不再需要多层.then方法,假设一个业务分很多步骤完成,并且每个步骤都是异步,依赖上一个步骤的结果。

function takeLongTime(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 200), n);
    });
}

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(n) {
    console.log(`step2 with ${n}`);
    return takeLongTime(n);
}

function step3(n) {
    console.log(`step3 with ${n}`);
    return takeLongTime(n);
}

// Promise方式
function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => step2(time2))
        .then(time3 => step3(time3))
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

doIt();

// async await方式
async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    const result = await step3(time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}
doIt();

//执行结果为:
//step1 with 300
//step2 with 500
//step3 with 700
//result is 900
//doIt: 1510.2490234375ms

你可能感兴趣的:(javascript,前端)