Promise和async/await

目录

一、什么是promise,及其作用

二 、Promise 基本流程

三、Promise的链式调用

四、Promise 的基本使用

五、Promise几种常用方法

5.1 Promise.all()

5.2 Promise.race()

5.3 Promise.any()

5.4 Promise.allSettled()

六、什么是Async/Await,及其作用

6.1 async 函数

6.2 await 表达式

七、区别:

1)简洁的代码

2) 错误处理:

3)条件语句

4)中间值

6)调试


一、什么是promise,及其作用

Promise是ES6中的一个内置对象,实际是一个构造函数,是JS中进行异步编程的新的解决方案。

  • 特点:
    ① 三种状态:pending(进行中)resolved(已完成)rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都不能改变这个状态。
    ② 两种状态的转化:其一,从pending(进行中)到resolved(已完成)。其二,从pending(进行中)到rejected(已失败)。只有这两种形式的转变。
    Promise构造函数的原型对象上,有then()和catch()等方法,then()第一个参数接收resolved()传来的数据,catch()第一个参数接收rejected()传来的数据
  • 作用:
    ① 通常用来解决异步调用问题
    ② 解决多层回调嵌套的方案
    ③ 提高代码可读性、更便于维护


二 、Promise 基本流程

三、Promise的链式调用

我们可以用 promise.then()promise.catch()promise.finally()这些方法将进一步的操作与一个变为已敲定状态的 promise 关联起来。这些方法还会返回一个新生成的promise 对象,这个对象可以被非强制性的用来做链式调用,就像这样:

const myPromise =
  (new Promise(myExecutorFunc))
  .then(handleFulfilledA,handleRejectedA)
  .then(handleFulfilledB,handleRejectedB)
  .then(handleFulfilledC,handleRejectedC);

// 或者,这样可能会更好...

const myPromise =
  (new Promise(myExecutorFunc))
  .then(handleFulfilledA)
  .then(handleFulfilledB)
  .then(handleFulfilledC)
  .catch(handleRejectedAny);

过早地处理被拒绝的 promise会对之后promise的链式调用造成影响。不过有时候我们因为需要马上处理一个错误也只能这样做。另一方面,在没有迫切需要的情况下,可以在最后一个.catch()语句时再进行错误处理,这种做法更加简单。

四、Promise 的基本使用

示例,如果当前时间是偶数就代表成功,否则代表失败

// 1. 创建一个新的Promise对象
const p = new Promise((resolve, reject) => { // 执行器函数,同步执行
    // 2. 执行异步操作任务
    setTimeout(() => {
        const time = Date.now() // 如果当前时间是偶数就代表成功,否则代表失败
        // 3.1 如果成功了,调用resolve(value)
        if (time % 2 === 0) {
            resolve('成功的数据,value = ' + time)
        } else {
            // 3.2 如果失败了,调用reject(reason)
            reject('失败的数据,reason = ' + time)
        }

    }, 1000);
})

p.then(value => {
    // 接受得到成功的value数据,专业术语:onResolved
    console.log('成功的回调', value)
}, reason => {
    // 接受得到失败的reason数据,专业术语:onRejected
    console.log('失败的回调', reason)
})

五、Promise几种常用方法

5.1 Promise.all()

将多个Promise封装成一个新的Promise,成功时返回的是一个结果数组失败时返回的是最先rejected状态的值。

使用场景:一次发送多个请求并根据请求顺序获取和使用数据

let promise1 = Promise.resolve(1);
  let promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('promise2');
    }, 2000);
  })
  let promise3 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('promise3');
    }, 1000);
  })
  let promise4 = new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('promise4');
    }, 1000);
  })
  let promise5 = Promise.reject('promise5');

 Promise.all([promise1, promise2, promise3]).then((values) => {
  //三个promise都是成功的,所以会输出一个数组
    console.log(values)    // [1, "promise2", "promise3"]
  }) 
  Promise.all([promise1, promise2, promise3, promise4]).then((values) => {
    console.log(values)  
  }, (reason) => {
  //promise4是失败的,所以只会返回promise4的失败结果
    console.log(reason)    //promise4
  })

  Promise.all([promise1, promise2, promise3, promise4, promise5]).then((values) => {
    console.log(values)
  }, (reason) => {
  //promise4个promise5都是失败的,但是promise5比promise4最先失败,所以返回promise5的失败结果
    console.log(reason)    //promise5
  })

5.2 Promise.race()

返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

简单来说,就是多个Promise中,哪个状态先变为成功或者失败,就返回哪个Promise的值

let promise1 = new Promise((resolve, reject) => {
  //setTimeout第三个以后的参数是作为第一个`func()`的参数传进去,promise1作为resolve的参数
    setTimeout(resolve, 500, 'promise1');
  });

  let promise2 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'promise2');
  });

  let promise3 = new Promise((resolve, reject) => {
    setTimeout(reject, 500, 'promise3');
  });

  let promise4 = new Promise((resolve, reject) => {
    setTimeout(reject, 100, 'promise4');
  });

  Promise.race([promise1, promise2]).then((value) => {
    console.log(value);     //promise2 100 <promise1 500  所以输出: promise2
  });  

  Promise.race([promise3, promise4]).then((value) => {
  }, (rejected) => {
    console.log(rejected)   //promise4 100 < promise3 500  所以输出: promise4
  });

  Promise.race([promise2, promise3]).then((value) => {
    console.log(value)      //promise2 100 < promise3 500   所以会走成功状态,输出: promise2
  }, (rejected) => {
    console.log(rejected)
  });

  Promise.race([promise1, promise4]).then((value) => {
    console.log(value)
  }, (rejected) => {
    console.log(rejected)   //promise4 100 < promise1 500   所以会走失败状态,输出: promise4
  });

5.3 Promise.any()

Promise.any() 接收一个Promise可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的 promise 。如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promiseAggregateError类型的实例,它是 Error 的一个子类,用于把单一的错误集合在一起。本质上,这个方法和Promise.all()是相反的

这个方法用于返回第一个成功的 promise 。只要有一个 promise 成功此方法就会终止,它不会等待其他的 promise 全部完成
不像 Promise.all()会返回一组完成值那样(resolved values),我们只能得到一个成功值(假设至少有一个 promise 完成)。当我们只需要一个 promise 成功,而不关心是哪一个成功时此方法很有用的。
同时, 也不像 Promise.race()总是返回第一个结果值(resolved/reject)那样,这个方法返回的是第一个* 成功的* 值。这个方法将会忽略掉所有被拒绝的 promise,直到第一个 promise 成功。


const pErr = new Promise((resolve, reject) => {
  reject("总是失败");
});

const pSlow = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, "最终完成");
});

const pFast = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "很快完成");
});

Promise.any([pErr, pSlow, pFast]).then((value) => {
  console.log(value);
  // pFast fulfils first
})
// 期望输出: "很快完成"

5.4 Promise.allSettled()

Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilledrejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。
当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个promise的结果时,通常使用它。
相比之下,Promise.all() 更适合彼此相互依赖或者在其中任何一个reject时立即结束。

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

Promise.allSettled(promises).
  then((results) => results.forEach((result) => console.log(result.status)));

// expected output:
// "fulfilled"
// "rejected"

六、什么是Async/Await,及其作用

① async/await是ES7新特性
② async/await是写异步代码的新方式,以前的方法有回调函数和Promise
③ async/await是基于Promise实现的,它不能用于普通的回调函数
④ async/await与Promise一样,是非阻塞
⑤ async/await使得异步代码看起来像同步代码,这正是它的魔力所在

async function 用来定义一个返回 AsyncFunction 对象的异步函数。异步函数是指通过事件循环异步执行的函数,它会通过一个隐式的 Promise 返回其结果,。如果你在代码中使用了异步函数,就会发现它的语法和结构会更像是标准的同步函数。

await 操作符用于等待一个 Promise 对象。它只能在异步函数 async function 中使用。

6.1 async 函数

async 函数的返回值为 Promise 对象,async 函数返回的 Promise 的结果由函数执行的结果决定

async function fn1() {
    return 1
}
const result = fn1()
console.log(result) // Promise { 1 }

既然是 Promise 对象,那么我们用 then 来调用,并抛出错误,执行 **onRejected() **且 reason 为错误信息为“我是错误”

async function fn1() {
    // return 1
    // return Promise.resolve(1)
    // return Promise.reject(2)
    throw '我是错误'
}
fn1().then(
    value => { console.log('onResolved()', value) },
    reason => { console.log('onRejected()', reason) } // onRejected() 我是错误
)

6.2 await 表达式

await 右侧的表达式一般为 promise 对象, 但也可以是其它的值

  1. 如果表达式是 promise 对象, await 返回的是 promise 成功的值
  2. 如果表达式是其它值, 直接将此值作为 await 的返回值

function fn2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1000)
        }, 1000);
    })
}
function fn4() { return 6 }
async function fn3() {
    // const value = await fn2() // await 右侧表达式为Promise,得到的结果就是Promise成功的value
    // const value = await '还可以这样'
    const value = await fn4()
    console.log('value', value)
}
fn3() // value 6

await 必须写在 async 函数中, 但 async 函数中可以没有 await,如果 await 的 Promise 失败了, 就会抛出异常, 需要通过 try...catch 捕获处理。

function fn2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            // resolve(1000)
            reject(1000)
        }, 1000);
    })
}
async function fn3() {
    try {
        const value = await fn2()
    } catch (error) {
        console.log('得到失败的结果', error)
    }
}
fn3() // 得到失败的结果 1000

七、区别:

1)简洁的代码

使用async函数可以让代码简洁很多,不需要像Promise一样需要些then,不需要写匿名函数处理Promise的resolve值,也不需要定义多余的data变量,还避免了嵌套代码。

2) 错误处理:

Promise 中不能自定义使用 try/catch 进行错误捕获,但是在 Async/await 中可以像处理同步代码处理错误

//#Promise
const makeRequest = () => {
  try {
    getJSON()
      .then(result => {
        // this parse may fail
        const data = JSON.parse(result)
        console.log(data)
      })
      // uncomment this block to handle asynchronous errors
      // .catch((err) => {
      //   console.log(err)
      // })
  } catch (err) {
    console.log(err)
  }
}
//#Async/await
const makeRequest = async () => {
  try {
    // this parse may fail
    const data = JSON.parse(await getJSON())
    console.log(data)
  } catch (err) {
    console.log(err)
  }
}

3)条件语句

条件语句也和错误捕获是一样的,在 Async 中也可以像平时一般使用条件语句

//#Promise
const makeRequest = () => {
  return getJSON()
    .then(data => {
      if (data.needsAnotherRequest) {
        return makeAnotherRequest(data)
          .then(moreData => {
            console.log(moreData)
            return moreData
          })
      } else {
        console.log(data)
        return data
      }
    })
}

//#Async
const makeRequest = async () => {
  const data = await getJSON()
  if (data.needsAnotherRequest) {
    const moreData = await makeAnotherRequest(data);
    console.log(moreData)
    return moreData
  } else {
    console.log(data)
    return data    
  }
}

4)中间值

在一些场景中,也许需要 promise1 去触发 promise2 再去触发 promise3,这个时候代码应该是这样的

const makeRequest = () => {
  return promise1()
    .then(value1 => {
      // do something
      return promise2(value1)
        .then(value2 => {
          // do something          
          return promise3(value1, value2)
        })
    })
}

如过 promise3 不需要 value1,嵌套将会变得简单。如果你有强迫症,则将值1&2使用 promise.all() 分装起来。

const makeRequest = () => {
  return promise1()
    .then(value1 => {
      // do something
      return Promise.all([value1, promise2(value1)])
    })
    .then(([value1, value2]) => {
      // do something          
      return promise3(value1, value2)
    })
}

但是使用 Async 就会变得很简单

const makeRequest = async () => {
  const value1 = await promise1()
  const value2 = await promise2(value1)
  return promise3(value1, value2)
}

错误栈

如过 Promise 连续调用,对于错误的处理是很麻烦的。你无法知道错误出在哪里。

const makeRequest = () => {
  return callAPromise()
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => {
      throw new Error("oops");
    })
}

makeRequest()
  .catch(err => {
    console.log(err);
    // output
    // Error: oops at callAPromise.then.then.then.then.then (index.js:8:13)
  })

但是对于 Async 就不一样了

const makeRequest = async () => {
  await callAPromise()
  await callAPromise()
  await callAPromise()
  await callAPromise()
  await callAPromise()
  throw new Error("oops");
}

makeRequest()
  .catch(err => {
    console.log(err);
    // output
    // Error: oops at makeRequest (index.js:7:9)
  })

6)调试

async/await能够使得代码调试更简单。2个理由使得调试Promise变得非常痛苦:

  • 《1》不能在返回表达式的箭头函数中设置断点
  • 《2》如果你在.then代码块中设置断点,使用Step Over快捷键,调试器不会跳到下一个.then,因为它只会跳过异步代码。

使用await/async时,你不再需要那么多箭头函数,这样你就可以像调试同步代码一样跳过await语句。


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值