Promise

Promise:
专门保证多个异步任务必须顺序执行,且解决了回调地狱问题的一项技术。
回调地狱问题:由于多层回调函数的嵌套,形成的很深的嵌套结构,就称之为回调地狱。
用回调函数的方式:
1.第一个任务把第二个任务装起来,暂不执行,第二个任务把第三个任务装起来,也不执行。
2.第一个任务最后一句话执行完毕,主动调用装有第二个任务的函数。
3.第二个任务最后一句话执行完毕,主动调用装有第三个任务的函数。
示例:使用回调函数解决多个异步任务顺序的执行

	function liang(next){//亮哥:异步
      console.log(`liang起跑...`);
      setTimeout(function(){//异步:在主程序之外执行,主程序不会等它执行完
        console.log(`liang到达终点!`);//亮最后执行的一句话
        //之后,主动调用下一项任务
        next();//下一项任务才开始执行
      },6000)
    }
    function ran(next){//然哥:异步
      console.log(`ran起跑...`);
      setTimeout(function(){//异步: 在主程序之外,主程序和其他程序都不会等待它执行完
        console.log(`ran到达终点!`);//然最后执行的一句话
        //之后,主动调用下一项任务
        next(); //下一项任务才开始执行
      },4000)
    }
    function dong(){//东哥:异步
      console.log(`dong起跑...`);
      setTimeout(function(){//异步: 在主程序之外
        console.log(`dong到达终点!`)
      },2000)
    }

    //希望先亮跑,亮到达终点后,然跑,然到达终点后,东跑
    //错误: 
    // liang();
    // ran();
    // dong();
    //正确: 
    liang(
      /*next=*/function(){
        ran(
          /*next=*/function(){
            dong();
          }
        );//ran的next=function(){dong();}
      }
    );//liang的next=function(){ran();}
    //当liang执行完最后一句话,之后自调用liang的next()
    //其实就是调用function(){ran();},函数中的ran()开始执行
    //当ran执行完最后一句话,之后自动调用ran的next()
    //其实就是调用function(){dong();},函数中的dong()开始执行

何时用promise:只要多个异步函数必须顺序执行时,都用promise代替回调函数方式。
使用promise实现多个异步任务顺序执行,异步间任务传参,在前后两个异步任务之间传参,参数解构传多个变量。
如何:
(0). 所有异步函数不要加回调函数参数了!
(1). 用new Promise(function(door){ … })包裹住原来的异步函数代码。
强调: 原异步函数不需要做任何改变
(2). 在原异步函数最后执行的一句话之后,调用door开门!
结果: 自动执行.then()中串联的后续任务(下一个格子间,也可能是下一个函数)
(3). new Promise()是放在函数内创建的,所以,如果希望外部直到这里有一个格子间的对象可以串联,必须用return new Promise()将其返回到函数外部!
function 前一项异步函数(){
return new Promise(function(door){
原异步函数内容
异步函数最后一句话之后: door()
})
}
(4). 调用return new Promise()所在的异步任务函数,既可以执行异步函数的任务,又可以返回格子间,可用.then()与下一项任务串联
前一项任务().then(下一项任务)
//下一项任务不要加(),因为不是立刻执行!
(5). 多一个异步任务串联: 任务1().then(任务2).then(任务3)
示例:使用promise实现多个异步任务顺序执行

	function liang(){//亮哥:异步
             //  格子间           赠门
      return new Promise(function(door){
        console.log(`liang起跑...`);
        setTimeout(function(){//异步
          console.log(`liang到达终点!`);//亮最后执行的一句话
          //之后,主动调用door开门!
          door();//自动执行.then()中串联的下一项任务
        },6000)
      })
    }
    function ran(){//然哥:异步
            //   格子间            门
      return new Promise(function(door){
        console.log(`ran起跑...`);
        setTimeout(function(){//异步
          console.log(`ran到达终点!`);//然最后执行的一句话
          //之后,主动调用door
          door(); //通知.then中串联的下一项任务可以开始执行!
        },4000)
      })
    }
    function dong(){//东哥:异步
      console.log(`dong起跑...`);
      setTimeout(function(){//异步: 在主程序之外
        console.log(`dong到达终点!`)
      },2000)
    }

    //先把亮和然串起来
    liang().then(ran).then(dong)
    //return
    //new Promise
    //同时
    //执行liang的任务
    //liang任务执行完
    //调用door()开门 -> 自动执行.then()中的ran()
    //                  ran() return new Promise
    //                       .then(dong)
    //                        同时执行ran()的内容
    //                        ran()执行完调用door()
    //                        开门!.then()后的dong()
    //                                    才开始执行

异步任务间传参:
(1). 其实door()开门时,是可以顺便传参的: door(实参值)
(2). 下一项任务,在自己的形参列表中定义形参变量接住上一个任务door()传过来的实参值。
示例: 在前后两个异步任务之间传参

	function liang(){//亮哥:异步
      return new Promise(function(door){
        var bang="接力棒";
        console.log(`liang拿着 ${bang} 起跑...`);
        setTimeout(function(){//异步
          console.log(`liang拿着 ${bang} 到达终点!`);//亮最后执行的一句话
          //之后,主动调用door开门!
          //    发
          door(bang);//自动执行.then()中串联的下一项任务
        },6000)
      })
    }
    //            接
    function ran(bang2){//然哥:异步
      //bang2=liang中door()给的"接力棒"
      return new Promise(function(door){
        console.log(`ran拿着 ${bang2} 起跑...`);
        setTimeout(function(){//异步
          console.log(`ran拿着 ${bang2} 到达终点!`);//然最后执行的一句话
          //之后,主动调用door
          //    发
          door(bang2); //通知.then中串联的下一项任务可以开始执行!
        },4000)
      })
    }
    //             接
    function dong(bang3){//东哥:异步
      //bang3=ran()中door()传来的"接力棒"
      console.log(`dong拿着 ${bang3} 起跑...`);
      setTimeout(function(){//异步: 在主程序之外
        console.log(`dong拿着 ${bang3} 到达终点!`)
      },2000)
    }

    //先把亮和然串起来
    liang().then(ran).then(dong)

坑: door()中只能接受一个变量!
(1). 如果传多个变量,也只有第一个变量的值,可以传到下一个任务!
(2). 如果硬要传多个值,可以放在数组或对象中传递:
door([值1,值2,…]) 或 door({属性1:值1, 属性2:值2,…})
参数解构 参数解构
function 下一项任务([形参1, 形参2]) 或 ({属性1, 属性2})
(3). 示例: 在前后两个异步任务之间传多个值:

	function liang(){//亮哥:异步
      return new Promise(function(door){
        var bang="接力棒";
        var qkl="巧克力";
        console.log(`liang拿着 ${bang} 起跑...`);
        setTimeout(function(){//异步
          console.log(`liang拿着 ${bang} 到达终点!`);//亮最后执行的一句话
          //之后,主动调用door开门!
          //      发
          //       0    1
          //door([bang,qkl]);//自动执行.then()中串联的下一项任务
          //  属性名:属性值 属性名:属性值
          //door({bang:bang,    qkl:qkl});
          door({bang, qkl})
        },6000)
      })
    }
    //            接
    //               0    1
    //function ran([bang,qkl]){//然哥:异步
    //          属性名:形参
    //            配对 接值
    //function ran({bang:bang, qkl:qkl}){
    function ran({bang,qkl}){
      //bang=liang中door({})中bang给的"接力棒"
      //qkl=liang中door({})中qkl给的"巧克力"
      return new Promise(function(door){
        console.log(`ran吃了${qkl}`);
        console.log(`ran拿着 ${bang} 起跑...`);
        setTimeout(function(){//异步
          console.log(`ran拿着 ${bang} 到达终点!`);//然最后执行的一句话
          //之后,主动调用door
          //    发
          door(bang); //通知.then中串联的下一项任务可以开始执行!
        },4000)
      })
    }
    //             接
    function dong(bang3){//东哥:异步
      //bang3=ran()中door()传来的"接力棒"
      console.log(`dong拿着 ${bang3} 起跑...`);
      setTimeout(function(){//异步: 在主程序之外
        console.log(`dong拿着 ${bang3} 到达终点!`)
      },2000)
    }

    //先把亮和然串起来
    liang().then(ran).then(dong)

错误处理:
(1). 其实: new Promise赠送了两个门:
a. door,通往正常路线下一个.then的门
b. err, 通往出错路线下一个.catch的门
c. 所以: new Promise(function(door, err){ … })
(2). 在异步任务中:
a. 如果正常执行,开door(实参值),接下来走.then()
b. 如果出错,开err(错误消息),接下来走最后一个.catch()
(3). 接住错误消息:
.then(…).then(…).catch(function(errMsg){ … })
示例: 模拟其中一个异步任务出错的情况:

	function liang(){//亮哥:异步
      //                赠两个门  正确  出错
      return new Promise(function(door,err){
        var bang="接力棒";
        console.log(`liang拿着 ${bang} 起跑...`);
        setTimeout(function(){//异步
          //用随机数模拟出错的概率
          var r=Math.random();//0~1
          //假设r<0.6 60% 可以正常执行
          //如果没有摔倒
          if(r<0.6){
            console.log(`liang拿着 ${bang} 到达终点!`);//亮最后执行的一句话
            //之后,主动调用door开门!
            door(bang);//自动执行.then()中串联的下一项任务
          }else{//否则如果摔倒了r>0.4 40%
            //而是走错误处理的门
            err("liang摔倒了!退赛!")
          }
        },6000)
      })
    }
    function ran(bang2){//然哥:异步
      //bang2=liang中door()给的"接力棒"
      return new Promise(function(door,err){
        console.log(`ran拿着 ${bang2} 起跑...`);
        setTimeout(function(){//异步'
          //用随机数模拟出错的概率
          var r=Math.random();//0~1
          //假设r<0.6 60% 可以正常执行
          //如果没有摔倒
          if(r<0.6){
            console.log(`ran拿着 ${bang2} 到达终点!`);//然最后执行的一句话
            //之后,主动调用door
            door(bang2); //通知.then中串联的下一项任务可以开始执行!
          }else{//否则如果摔倒了r>0.4 40%
            //而是走错误处理的门
            err("ran摔倒了!退赛!")
          }
        },4000)
      })
    }
    function dong(bang3){//东哥:异步
      //bang3=ran()中door()传来的"接力棒"
      console.log(`dong拿着 ${bang3} 起跑...`);
      setTimeout(function(){//异步: 在主程序之外
        console.log(`dong拿着 ${bang3} 到达终点!`)
      },2000)
    }

    //先把亮和然串起来
    liang().then(ran).then(dong).catch(function(errMsg){
      console.error(errMsg);
    })
Promise的三个状态(学名): 
(1). 三个状态: pending(挂起/等待), fulfilled(成功), rejected(失败)
(2). 其实new Promise赠给我们的两个门也是有学名的:
new Promise(function(resolve, reject){ ... })
                   同意   拒绝
(3). 三个状态的切换: 
a. 执行new Promise()时,异步任务执行完之前,promise对象处于pending状态
b. 当异步任务执行成功,我们调用resolve()开门时,整个promise对象就变成fulfilled状态,就自动调用.then()中的下一项任务
c. 当异步任务执行过程中出错,我们调用reject()开门时,整个promise对象就变成rejected状态,就不再调用.then(),而是转向调用结尾的.catch()

多个异步任务同时执行,但是只有在最后一个任务执行完才做一件事情。
(1). 错误: 顺序调用多个异步任务,在最后执行想要执行的代码。
(2). 因为: 主程序中的代码绝对不会等异步任务执行完才执行。一定是,多个异步任务刚开始执行时,主程序的代码就会立刻抢先执行!
(3). 正确: Promise.all([格子间1, 格子间2, 格子间3, …]).then(最后一项任务)
(4). 原理:
a. all后的数组中每个格子间的异步任务都是并发执行的!谁也不等谁!
b. 但是,all后的.then()注定会等最慢的一个格子间执行完,才自动执行!
(5). 示例: 等待多个异步任务并发执行,都结束后才执行一项任务:

	function liang(){//亮哥:异步
      return new Promise(function(resolve){
        console.log(`liang起跑...`);
        setTimeout(function(){//异步
          console.log(`liang到达终点!`);//亮最后执行的一句话
          //之后,主动调用resolve开门!
          resolve();//自动执行.then()中串联的下一项任务
        },6000)
      })
    }
    function ran(){//然哥:异步
      return new Promise(function(resolve){
        console.log(`ran起跑...`);
        setTimeout(function(){//异步
          console.log(`ran到达终点!`);//然最后执行的一句话
          //之后,主动调用resolve
          resolve(); //通知.then中串联的下一项任务可以开始执行!
        },4000)
      })
    }
    function dong(){//东哥:异步
      return new Promise(function(resolve){
        console.log(`dong起跑...`);
        setTimeout(function(){//异步: 在主程序之外
          console.log(`dong到达终点!`);
          resolve();
        },2000)
      })
    }

    //希望三个人百米赛跑(谁也不等谁),但是最后一个人跑完时,才输出比赛结束!
    //错误: 
    // liang();//异步
    // ran();//异步
    // dong();//异步
    // console.log(`比赛结束!`);
    //正确: 
    Promise.all([
      //只有调用异步函数,才能返回new Promise()格子间
      liang(),//return new Promise()
        //resolve()
      ran(),//return new Promise()
        //resolve()
      dong()//return new Promise()
        //resolve()
    ]).then(function(){ console.log("比赛结束!") })

Promise.all的返回值:
a. 接: Promise.all([…]).then(function(arr){ … })
b. arr中接到的返回值的顺序: 和all中异步函数的顺序一致,与执行结束的先后顺序无关!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值