目录

思维导图

Promise是什么?

为什么要用promise?

详细讲Promise

如何使用promise

解决回调地狱

promise的关键问题

async与await

关于async/await中的同步异步

.Promise中的then方法是如何实现同步异步的?

延迟函数/休眠函数如何实现


思维导图

 

Promise是什么?

Promise:承诺者模式(承诺异步编程结束后,根据处理的状态和结果,通知fulfilled/reject)执行  它是ES6新增的一个内置类,基于Promise可以有效管理“异步编程”,避免回调地狱

  • 从语法上来说promise是一个构造函数
  • 从功能上来说promise对象来封装一个异步操作,并可获取其成功/失败的结果值

兼容问题

new promise(...) 不兼容ie浏览器(EDGE可以兼容) 真实项目开发的时候,如果使用了promise还要兼用ie浏览器需要处理兼容

  • @1 babel/preset语法包,无法编译promise 他只能编译 let/const这种常规语法
  • @2 我们需要基于@babel/polyfill实现promise的兼容处理(原理基于es5手动实现的一个promise)

什么是回调地狱?

 JQ中的ajax管理,是基于回调函数的方式管理的「请求成功后,会触发success回调函数执行,result就是本次请求获取的结果」;

串行:如果我们想实现ajax串行,需要把下一个请求发送,放在上一个请求成功的回调函数中处理,如果有多个串行的请求,就会一层层的嵌套... => “回调地狱”「代码看起乱、不方便管理」

并行:请求之间没有啥依赖,可以同时发送多个请求(一般会额外处理一些事情:等待所有请求都成功,统一干啥事)

痛点:传统方案中,基于回调函数的方式管理异步编程的代码,总是要在异步任务可执行的时候,在他的回调函数中处理一些事情,这样很容易就产生回调地狱!!

   // 存在三个:a/b/c.txt文件,想通过a文件获取c文件中的内容,
            // ,如果用普通的ajax,就会产生ajax中嵌套 
            $(function(){
                $.get("a.txt",function(data){//第一层嵌套
                    $.get(`${data}.txt`,function(data){//第二层嵌套
                        $.get(`${data}.txt`,function(data){//第三层嵌套
                            console.log(data);
                        })
                    })
                })

 promise解决回调地狱

   const http1 = () => {
        return new Promise(resolve => {
            $.ajax({
                url: '/api/test1',
                success: resolve
            });
        });
    };
    const http2 = () => {
        return new Promise(resolve => {
            $.ajax({
                url: '/api/test2',
                success: resolve
            });
        });
    };
    const http3 = () => {
        return new Promise(resolve => {
            $.ajax({
                url: '/api/test3',
                success: resolve
            });
        });
    };
    http1()
        .then(value => {
            console.log('第一个请求结果', value);
            return http2()
        })
        .then(value => {
            console.log('第二个请求结果', value);
            return http2()
        })
        .then(value => {
            console.log('第三个请求结果', value);
            return http3()
        });
    (async function () {
        let value = await http1()
        console.log('第一个请求结果', value);
        value = await http2()
        console.log('第二个请求结果', value);
        value = await http3()
        console.log('第三个请求结果', value);
    })()

为什么要用promise?

1.指定回调函数的方式更加灵活

  • 旧的:必须在启动异步任务前指定
  • promise 启动异步任务==>返回promis对象==>给promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)

2.支持链式调用,可以解决回调地狱问题

详细讲Promise

一.实例对象私有属性方法

promise的状态

实例对象中的一个属性:[[PromiseState]]

  •   - ==pending==:等待状态 (默认)
  •   - ==fulfilled/resolved==:成功状态
  •   - ==rejected==:失败状态

pending变为resolved

pending变为reject

说明:只有两种且一个promise只能改变一次

        无论成功失败都有一个结果数据

        成功的数据一般称为value失败的结果数据一般称为reason

PromiseResult promise对象的值

  • 实例对象中的另一个属性[PromiseResult]
  • 保存着异步任务[成功/失败]的结果        
  • 状态为padding时,默认是undefined

promise执行过程

 如何使用promise

二.promise构造函数new Promise([executor]);  

  • executor:必须是一个函数,传递的不是函数会报错
  • executor函数执行的时候,会接收两个实参值,我们分别用resolve/reject形参来存储
  • executor函数:执行器(resolve,reject)=>{}
  • resolve函数:内部定义成功时我们调用函数value=>{},把创建的实例状态改为成功态(fulfilled)实例的值为value成功的结果
  • reject函数:内部定义失败时我们调用函数reason=>{}把创建的实例状态改为失败态(rejected)实例的值为reason失败的原因
  • 如果executor函数执行过程中,有异常错误抛出,则实例的状态仍然是rejected实例的值是报错的原因
  • 一但状态改为fulfilled或者rejected则不能在次修改
  • 说明executor会在promise内部立即同步调用,异步操作在执行器中进行
        let p = new Promise((resolve, reject) => {
         // 同步调用
           console.log(111);
        })
        console.log(222);

三. 原型对象属性方法(实例可以调用的公有属性方法)

promise.prototype.then方法(onResolved,onRejected)=>{}

  • promise实例.then([onResolved],[onRejected])
  • 实例状态为成功则把onResolved函数执行,反之实例状态是失败onRejected函数执行并且把实例状态的值(成功的结果失败的原因)作为实参传递给某个函数
  • 改变promise实例的状态和值,就是为了控制then中的两个方法谁来执行,以及实参值value/reson是多少
  • onResolved函数:成功的回调函数(value)=>{}
  • onRejected函数:失败的回调函数(reason)=>{}
  • 说明:指定用于value的成功回调和用于的到失败reason的失败回调返回一新的promise对象,主要目的是为了实现链式写法
  • 新的promise对象它的状态和值有上一个实例.then传递的onfulfilled或者onrejected执行决定
     let p = new Promise((resolve, reject) => {
            // resolve(console.log(a)); 语法错误失败
            resolve("成功")
            reject("失败")
        })
        p.then((value) => { console.log(value); },
            (reason) => { console.log(reason); })

promise.prototye.catch方法:(onRejected)=>{}

onRejected函数:失败的回调函数(reason)=>{},

如果实例状态是失败,而我们也没有去设置onreject进行处理,那么控制台会抛出异常,但是不影响其他代码执行,所以我们要在then链的最末尾设置一个catch,这样不论哪一级返回失败的promise实例,都会把catch的onreject执行

        let p = new Promise((resolve, reject) => {
            reject("失败")
        })
        p.catch((reason) => {
            console.log(reason);
        })

 promise.prototye.finally方法:

不论成功失败都会走finally方法

 promise.prototye.symbol(Symbol.toStringTag:Promise)方法

四.promis作为普通对象,具备静态私有属性方法

Promise.resolve:方法(value)=>{}

  • value成功的数据或promise对象
  • 如果传入的参数为 非promise类型的对象,则返回的结果为非promise对象
  • 如果传入的参数为promise对象,则参数的结果决定了resolve的结果
  • 说明:返回一个成功或失败的promise对象
   let p1 = Promise.resolve(521)
        //如果传入的参数为 非promise类型的对象,则返回的结果为非promise对象
        //如果传入的参数为promise对象,则参数的结果决定了resolve的结果
        let p2 = Promise.resolve(new Promise((resolve, reject) => {
            // resolve("OK")
            reject("失败")
        }))
        console.log(p2);

Promise.reject方法(reason)=>{}

  • reason失败的原因
  • 说明:返回一个失败的promise对象 

        let p = Promise.reject(521)
        let p2 = Promise.reject("iloveyou")
        let p3 = Promise.reject(new Promise((resolve, reject) => {
            resolve("ok")
        }))
        console.log(p3);
        //失败的原因是成功的promise对象

promise.all方法[promises]=>{}

  • promises包含了n个promise的数组,如果其中一项不是promise实例,则浏览器也会默认把其变为状态是fulfilled,(成功)值是自身的实例
  • 说明:返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败的了就直接失败
        let p1 = new Promise((resolve, reject) => {
            resolve("ok")
        })
        let p2 = Promise.resolve("成功")
        let p3 = Promise.resolve("oh year")
        const result = Promise.all([p1, p2, p3])
        console.log(result);

promise.race方法[promises]=>{}

  • promises包含了n个promise的数组
  • 说明:返回一个新的promise,第一个完成的promise的结果是最终的结果状态

promise.any方法

集合中只要有一项是成功的,则返回一个新的promise值是成功项的值;所有项都是失败才是失败

promise.allSettled方法

解决回调地狱

- 注意事项:

  •   - 执行函数中执行resolve方法会修改为成功状态
  •   - 执行函数中执行rejecte方法或者有执行代码==报错==,会修改为失败状态
  •   - then方法无论执行哪种状态的回调函数,都会默认return一个新的Promise实例resolve状态的对象,默认值为undefined,通过return res的方式可以给下一次的函数中传值。
  •   - 如果想让then方法的回调函数返回状态为rejected的promise实例,就需要return new rejected对象,或者throw一个错误。
  function getdata(param){
                  return  new Promise(function(resolve,reject){
                  resolve(param);//修改状态为resolved/fulfilled
                  reject(200);//由于承诺者模式,一但状态修改过后,不会再次修改,所以不会执行它
              })
              }

              getdata(a).then(
                function(res){ return res },//处理p成功后的结果,res为传过来的参数100
                function(rej){ return rej})//处理p失败状态后的结果,rej为传过来的参数200
              .then(
                function(res){res},
                function(rej){rej})

promise的关键问题

1.如何改变promise的状态?

  • resolve(value)如果当前是pending就会改变为resolved
  • reject(reason)如果当前是pending就会改变为rejected
  • 抛出错误如果当前是pending就会改变为rejected

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

当promise改变为对应状态时都会调用

3.改变promise状态和指定回调谁先谁后?

都有可能,正常情况下指定回调在改变状态,但也可以先改状态在指定回调

如何先改状态在指定回调?,

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

什么时候才能得到数据?

  • 如果先指定回调那当状态发生改变时,回调函数会调用得到数据
  • 如果先改变状态,那当指定回调时,回调函数会调用,得到数据

4.promise.then()返回的promise的结果状态由什么决定?

  • 如果抛出异常, 新promise变为rejected,reason为抛出异常
  • 如果返回的是非promise的任意值,新promise变为resolved (成功),值是return返回的值没有写return 值为undefined
  • 如果返回的是另一个新的promise此promise的结果为新promise的结果

5.promise如何串联多个操作任务?,

  • promise的then()返回一个新的promise,可以开成then()的链式调用
  • 通过then的链式串联多个同步/异步任务

6.then链的穿透和顺延机制

  • 执行then如果ononfulfilled/onrejected没有传递则顺延到下一个同等状态的方法上
  • 当使用promise的then链式调用时,可以在最后指定失败原因
  • 前面的任何操作出了异常都会传到最后失败的回调中处理

7.如何中断promise链?

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

async与await

只能出现在函数中promise+generator的语法糖

async函数

  • 函数的返回值为promise对象
  • 如果函数中使用await,则函数必须经过async修饰
  • promise对象的结果由async函数的执行返回值决定
    async function main() {
        // 返回值是一个非promise对象
        return 521
        // 返回一个promise对象
        return new Promise((resolve, reject) => {
            // reject("Error")
            resolve("ok ")
        })
        //抛出异常
        throw "OH no"
    }
    let result = main();
    console.log(result);

await表达式

  • 可以把异步操作改为类似于同步的效果,
  • await右侧表达式一般为promise对象,但也可以是其他类型的值,
  • 如果表达式是promise对象 await返回的是promise成功的值
  • 如果表达式是其他的值,直接将此值作为await返回的值
  • 需要等待后面的promise实例状态为成功才会执行当前上下文await下面的代码,如果后面实例是失败的则下面代码不执行

注意

  • await必须写在async函数中但async函数可以没有await
  • 如果await的promise失败了 就会抛出异常通过 try  catch捕获处理,在catch中处理状态是失败要做的事情
  • 等待,我们一般在其后面放promise实例,她会等待实例状态为成功,再去执行“当前上下文”中,await下面的代码「如果promise实例管控的是一个异步编程,其实它是在等待异步成功,再执行下面代码,类似于把异步改为同步效果」

      async function main() {
            let p = new Promise((resolve, reject) => {
                // resolve("ok");
                reject('Error')
            })
            // 右侧为promise的情况
            // let res = await p
            //如果右侧为其他类型
            // let res2 = await 20
            //如果promise是失败状态
            try {
                let res = await p;
            } catch (e) {
                console.log(e);
            }
        }
        main()

关于async/await中的同步异步

await后面需要跟一个promise实例=>await100 也会变成promise.reslove(100)

当前上下文中await下面的代码,需要等待promise状态实例确定

  • 成功下面的代码可以执行
  • 失败/pending下面的代码不会执行

await下面代码是一个异步微任务

  • 当前上下文中,只遇到await把下面的代码设置为一个异步微任务,放在webAPI(消息队列)中监听
  • 接下来等到await后面的promise实例的状态,如果状态变为成功了,再把下面的异步微任务放在等待对列中排队等待,等主线程空闲下来之后,在拿出来执行

.Promise中的then方法是如何实现同步异步的?

 new Promise([exector]):exector执行函数是同步的

- 在执行到p.then()方法时,存在两种情况:

  •   - 情况一:**已知p的结果状态以及值**:但是没有立即执行[onfulfilled]/[onrejected],此时会创建一个可执行的异步微任务(==只存放p状态对应的方法==),放在WebAPI中监听,但是因为已经知道是可执行的,所以立即挪至EventQueue中排队等待...
  •   - 情况二:**还不知道p的结果状态以及值**:更无法知道该执行[onfulfilled]/[onrejected],此时先把p.then()方法存放在WebAPI中,直到p实例的状态确定后,再通知p.then()方法执行【通知也是一个异步微任务】
  • 后期我们基于reslove/reject把实例状态立即修改后,也并不会立即通知,事先存储[onfulfilled]/[onrejected]的方法执行,而是创建一个异步微任务

延迟函数/休眠函数如何实现

 延迟函数、休眠函数:表示当前上下文中,当执行到某函数时,会暂停代码执行,可以自己定义停止多长时间

- 使用async/await和promise来实现:

-----------------------------------------------完结----------------------------------------------------------------------

-----------------------接受大佬们的批改,欢迎留言评论-----------------------------------------------------------

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐