ES6 Promise总结与归纳——看这一篇就够了

一、什么是Promise

  1. Promise 是一种为了避免回调地狱的异步解决方案 (目前最先进的解决方案是async和await的搭配(ES8),它们也是基于promise的)
  2. 从语法上讲,Promise是一个对象或者说是构造函数,用来封装异步操作并可以获取其成功或失败的结果。
  3. Promise 是一种状态机制: pending(进行中)、fulfilled(已成功)和rejected(已失败) 只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态,即Promise的状态改变是不可逆的

Promise机制说明图:2个阶段,2个过程,3种状态

Promise机制说明图

二、为什么需要Promise

1、Promise出现之前

在ES6之前,对于异步问题的处理一般采用回调函数的形式,但由此产生的回调地狱问题常常影响开发者的开发体验。

2、回调地狱

回调地狱就是回调函数中嵌套回调函数,回调地狱是为了实现代码按顺序执行而出现的一种操作,但层层嵌套往往会造成我们的代码可读性非常差,后期不好维护。


// 回调地狱示例代码

function fn1(a) {
     setTimeout(() => {
         console.log(a);
         a--;
         setTimeout(() => {
             console.log(a);
             a--
             setTimeout(() => {
                 console.log(a);
                 a--
                 setTimeout(() => {
                     console.log(a);
                     a--
                     setTimeout(() => {
                         console.log(a);
                     }, 1000);
                 }, 1000);
             }, 1000);
         }, 1000);
     }, 1000);
 }
 fn1(5)
 

3、Promise标准出现

Promise最早是由社区提出和实现的一种解决异步编程的方案。ES官方参考了大量的异步场景,总结出了一套异步通用的标准模型,该模型可以覆盖几乎所有的异步场景甚至是同步场景,其可概括为2个阶段,2个过程,3种状态,再加上后续处理API(then、catch等),也就是我们所说的Promise。

三、使用Promise优雅的解决回调地狱

错误写法

并不是使用Promise就一定能解决回调地狱,例如下面这个例子,并没有去解决回调地狱,仍然造成了回调地狱

unction fn1(a) {
    new Promise((res, rej) => {
             res(a)
         })
         .then((params) => {
             return new Promise((res, rej) => {
                 setTimeout(() => {
                     console.log(params);
                     res(--params)
                     return new Promise((res, rej) => {
                         setTimeout(() => {
                             console.log(params);   res(--params)
                             return new Promise((res, rej) => {
                                 setTimeout(() => {
                                     console.log(params);   res(--params)
                                         return new Promise((res, rej) => {
                                          setTimeout(() => {
                                           console.log(params);   res(--params)
                                           return new Promise((res, rej) => {
                                             setTimeout(() => {
                                                 console.log(params);   res(--params)
                                                 }, 1000)
                                             })
                                         }, 1000)
                                   })
                                 }, 1000)
                             })
                         }, 1000)
                     })
                 }, 1000)
             })
         })

 }
 let a = 5
 fn1(a)

注意 优化成promise并不是在一个 .then里面 继续做操作 包括你在.then里继续new一个promise 并且一直嵌套这.then.then 还是会产生回调地狱,

正确写法

优化成 promise 发现就不会出现嵌套关系,而且不管写多少个 也是条条有序

    function fn1(a) {
          new Promise((res,rej)=>{
                res(a)
            })
            .then((params)=>{
              return  new Promise((res,rej)=>{
                setTimeout(() => {  console.log(params);res(--params) }, 1000) }) 
             })
             .then((params)=>{
              return  new Promise((res,rej)=>{
                setTimeout(() => {  console.log(params);res(--params) }, 1000) }) 
             })
             .then((params)=>{
              return  new Promise((res,rej)=>{
                setTimeout(() => {  console.log(params);res(--params) }, 1000) }) 
             })
             .then((params)=>{
              return  new Promise((res,rej)=>{
                setTimeout(() => {  console.log(params);res(--params) }, 1000) }) 
             })
             .then((params)=>{
              return  new Promise((res,rej)=>{
                setTimeout(() => {  console.log(params);res(--params) }, 1000) }) 
             })        
         }
         let a=5
         fn1(a)

实例中优化

在下面中的demo中 在一个.then中又嵌套了两次.then 这就造成了回调地狱 ,你不需要关心内容,只需要知道如何解决

  return new Promise((resolve, reject) => {
        if (ctx.rootState.user.profile.token) {
          // 没有修改登录后的sku接口
          const oldGoods = ctx.state.list.find(it => it.skuId === oldSkuId)
          console.log(newSku)
          // 删除 拿到之前的skuId删除
          deleteCart([oldSkuId]).then(() => {
            // 添加最新的sku进入购物车
            return insertCart({ skuId: newSku.value.skuId, count: oldGoods.count }).then(() => {
              // 请求最新的数据
              return findCartList().then((data) => {
                ctx.commit('setCartList', data.result)
                resolve()
              })
            })
          })

优化

return new Promise((resolve, reject) => {
        if (ctx.rootState.user.profile.token) {
          // 没有修改登录后的sku接口
          const oldGoods = ctx.state.list.find(it => it.skuId === oldSkuId)
          console.log(newSku)
          // 删除 拿到之前的skuId删除
          deleteCart([oldSkuId])
            .then(() => { // 添加最新的sku进入购物车
              insertCart({ skuId: newSku.value.skuId, count: oldGoods.count })
              console.log(1)
            })
            .then(() => { findCartList();  })
            .then((data) => {
              ctx.commit('setCartList', data.result)
              resolve()
            })

再次优化 省略箭头函数中的大括号

      return new Promise((resolve, reject) => {
        if (ctx.rootState.user.profile.token) {
          // 没有修改登录后的sku接口
          const oldGoods = ctx.state.list.find(it => it.skuId === oldSkuId)
          console.log(newSku)
          // 删除 拿到之前的skuId删除
          deleteCart([oldSkuId])
          // 添加最新的sku进入购物车
            .then(() => insertCart({ skuId: newSku.value.skuId, count: oldGoods.count }))
            .then(() => { findCartList(); console.log(2) })
            .then((data) => {
              ctx.commit('setCartList', data.result)
              resolve()
            })
      }
})

JS执行顺序——宏任务和微任务

Promise属于微任务,如果你对JS执行顺序感兴趣 JavaScript宏任务(macrotask)和 微任务(microtask) 执行顺序

四、async和await

语法糖

async 和 await 是 ES2017 新增两个关键字,它们借鉴了 ES2015 中生成器在实际开发中的应用,目的是简化 Promise api 的使用,并非是替代 Promise。

async

目的是简化在函数的返回值中对Promise的创建

async 用于修饰函数(无论是函数字面量还是函数表达式),放置在函数最开始的位置,被修饰函数的返回结果一定是 Promise 对象。


async function test(){
    console.log(1);
    return 2;
}

//等效于

function test(){
    return new Promise((resolve, reject)=>{
        console.log(1);
        resolve(2);
    })
}

await

await关键字必须出现在async函数中!!!!

await用在某个表达式之前,如果表达式是一个Promise,则得到的是thenable中的状态数据。


async function test1(){
    console.log(1);
    return 2;
}

async function test2(){
    const result = await test1();
    console.log(result);
}

test2();

等效于


function test1(){
    return new Promise((resolve, reject)=>{
        console.log(1);
        resolve(2);
    })
}

function test2(){
    return new Promise((resolve, reject)=>{
        test1().then(data => {
            const result = data;
            console.log(result);
            resolve();
        })
    })
}

test2();

如果await的表达式不是Promise,则会将其使用Promise.resolve包装后按照规则运行

Tips:

  1. 只要出现async,一定返回的是Promise,哪怕没有return,它返回的也是Promise,其resolve的就是undefined,在then中能打印出undefined;
  2. 如果要拒绝,要reject,就使用throw;
  3. 如果返回的就是Promise对象,会特殊处理,会直接用这个返回的Promise,相当于自动省略了async,没有必要这样操作;
  4. async中不一定有await,但await关键字一定是只出现在async中,且await必须是直接在async函数返回才有效;
  5. 有些情况,例如setTimeout,不能直接用async、await,需要先用Promise包装处理;

五、Promise个人总结

  1. Promise一定要有resolve或者reject,如果不写,就默认resolve()和reject(),即后续的data没有值。原生的Promise如果不resolve/reject就是pending,async就算不return也会默认resolve(undefined)。当然,如果有报错,reject()会自动接收错误并传递给下一环节。
  2. 无论resolve还是reject,一旦转换就无法改变,即如果Promise内部代码有报错,则后续resolve自动失效。同理,如果已经resolve了,则后续报错也不会传递给下一环节。
  3. 无论resolve还是reject,都不会中止代码执行,后续代码正常运行。
  4. Promise内部代码,本身是同步的,只有thenable和catchable代码是异步的。
  5. 同步resolve/reject,异步return。即只有在第1层new Promise中使用resolve/reject,后续的then中用return/throw。但如果在then中又返回一个new Promise函数并且调用的状态是reject(),就会失败rejected。
  6. then中的第二个参数,可以替代catch,如果同时写了then的第二个参数和catch,则只执行then的第二个参数,catch不再重复执行。系统默认报错和new Error()报错都能自动被reject()传递,如果没有错误处理函数就会爆红,有就按正常文本提示显示。
  7. 异步里是没有resolve/reject的,就算加了,也只是外层的,如果外层没有,就会报错。
  8. 同层级的链式then,会先把全部的第1层执行完,再执行第2层,再执行第3层,解决方法:可将需要后执行的加入后边的then队列中。。。或者可以不解决,只要它们的数据没有关联,先后无所谓(就像轮转时间片)。
  9. 总的来说,还是async-await好用,特别是await可以直接将异步函数,模拟成一种同步的效果(即一定要拿到await后的值,才执行下一步,即下一步可直接用它的结果)。当然,只是模拟出效果,其实它仍然是异步的。
  10. await之后的表达式要是Promise,如果不是,就会被Promise.resolve()包装后按规则运行。Promise.resolve(),该方法返回一个resolved状态的Promise,传递的数据作为状态数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值