上一篇我们提到了异步编程的基本概念,以及如何实现一个基本的Promise封装实例,接下来我们要了解的是关于Promise的使用过程中,我们容易触碰到的误区

Promise常见误区

表面看起来Promise的本质就是使用回调函数去定义异步任务结束后所需执行的任务,我们在之前的例子中是用then方法将回调函数传递进去的,Promise将我们的回调函数划分为两类,分别是成功过后的回调和失败过后的回调,如果此时我们需要串联多个异步回调仅凭then方法的嵌套调用我们将陷入无限嵌套的回调地狱之中。
而这种嵌套回调的方式是我们使用promise常见的错误之一,正确的方式是使用Promise then方法链式调用的特点来保证异步任务的扁平化。

Promise链式调用

Promise对象的then方法会返回一个全新的Promise对象,所以我们可以使用链式调用的方法去添加then方法
后面的then方法就是在为上一个then方法返回的Promise注册回调
前面then方法中回调函数的返回值会作为后面then方法回调的参数
如果回调中返回的是Promise对象,那后面的then方法的回调会等待它的结束

Promise异常处理

手动捕获的Promise异常

  1. // 通过Promise内部的onRejected来返回异常
  2. ajax('/users.json')
  3. .then(function onFulfiled(res){
  4. console.log('onFulfiled',res)
  5. },function onRejected(error){
  6. console.log('onRejected',error)
  7. })
  8. // 通过catch来捕捉异常,链式调用then方法更建议使用这种
  9. ajax('/users.json')
  10. .then(function onFulfiled(res){
  11. console.log('onFulfiled',res)
  12. })
  13. .catch(function onRejected(error){
  14. console.log('onRejected',error)
  15. })

全局捕获Promise异常

  1. // 浏览器中
  2. window.addEventListener('unhandledrejection',event=>{
  3. const { reason , promise } = event
  4. console.log(reason,promise)
  5. // reasom => Promise 失败原因,一般是一个错误对象
  6. // promise=》 出现异常的Promise对象
  7. event.preventDefault()
  8. },false)
  9. //node
  10. process.on('unhandledRejection',(reasin,promise)=>{
  11. console.log(reason,promise)
  12. // reason => Promise失败原因,一般是一个错误对象
  13. // promise => 出现异常的Promise对象
  14. })

这里并不推荐全局捕获

Promise并行执行

  1. var promise = Promise.all({
  2. ajax('users.json'),
  3. ajax('poster.json')
  4. })
  5. promise.then(function(values){
  6. console.log(values)
  7. }).catch(function(error){
  8. console.log(error)
  9. })
  10. // 穿行和并行同时使用
  11. ajax('/urls.json')
  12. .then(value=>{
  13. const urls = Object.values(value)
  14. const tasks = urls.map(url => ajax(url))
  15. return Promise.all(tasks)
  16. })
  17. .then(values=>{
  18. console.log(values)
  19. })

Promise.all()和Promise.race()都可以把多个Promise对象组合成一个Promise对象,不同的是

  • Promise.all()是等待组合的Promise所有任务结束过后才结束
  • Promise.race()是跟随任务中第一个任务完成结束而结束 ```json const request = ajax(‘/posts.json’) const timeout = new Promise((resolve,reject)=>{ setTimeout(()=>reject(new Error(‘timeout’)),500) })

Promise.race({ request, timeout }) .then(value=>{ console.log(value) }) .catch(error=>{ console.log(error) }) // 实现ajax超时处理的方式

  1. <a name="zrWqm"></a>
  2. ## Promise执行时序
  3. 即便在Promise的代码当中不存在异步操作,该Promise的回调操作依旧会进入到回调队列当中等待所有的同步任务执行完毕再去调用执行
  4. ```json
  5. console.log('global start')
  6. Promise.resolve()
  7. .then(()=>{
  8. console.log("promise")
  9. })
  10. console.log('global end')
  11. // global start
  12. // global end
  13. // promise

如果我们在这段代码中加入一个定时器,会发生另外一种情况

  1. console.log('global start')
  2. setTimeout(()=>{
  3. console.log('setTimeout')
  4. },0)
  5. Promise.resolve()
  6. .then(()=>{
  7. console.log("promise")
  8. })
  9. console.log('global end')
  10. // global start
  11. // global end
  12. // promise
  13. // seTimeout

我们发现定时器任务执行的打印操作晚于Promise回调的执行,其原因在于Promise的回调操作是以微任务的形式在代码中执行,而定时器是以宏任务的形式执行的。
在我们的日常开发过程中,我们所接触到的以异步调用的API基本都是作为宏任务的方式去执行的,而Promise对象和MutationBoserver对象,以及node中的process.nextTick则是以微任务的方式去执行的。