学习es6中Promise 对象

为什么要有Promise对象?

假设有这样的需求:用户必须登录成功后返回Token,之后拿着这个Token去查询用户列表,然后再选择某一个用户去查询在用户关联的角色列表(这里不考虑后端一步到位直接返回角色数据,而是通过前端多次网络请求去获取最终数据),在ES5中为实现一个任务执行完成后再去执行另一个任务,我们不得不在JavaScript 中层层地嵌套回调函数就如同下面的代码:

$.ajax({
    
    
	url: "/login"
    success:function(res1){
    
    
        $.ajax({
    
    
        	url: "/findUser"
            success:function(res2){
    
    
                $.ajax({
    
    
                	url: '/findRoleByUserId'
                    success:function(res3){
    
    
                    }
                });
            }    
        });
    }
});

如果同样的需求再多一点则会导致层层嵌套的函数过多,会导致代码可阅读性很差而且难以维护,ES6中新增的Promise,可以解决我们的上述的烦恼并让我们的代码更简洁、更便于阅读。

基本用法

  • 创建Promise对象
    可以使用Promise对象提供的构造器new 一个对象:
    在这里插入图片描述
    通过查看其源码可以发现创建对象时必须提供一个executor执行器对象,此执行器中内部又维护了两个函数对象:resolverejectresolve函数会在执行器执行成功后执行,而reject函数会在执行器执行失败后执行。
    let promise = new Promise((resolve,reject)=> {
    
    
      // 进行异步请求,例如向后台请求数据
       let success = false
      if(success) {
    
    
        console.log('我是请求成功后的逻辑')
        resolve('请求成功')
      } else {
    
    
        console.log('我是请求失败后的逻辑')
        reject('请求失败')
      }
    }).then(res=> console.log(res)).catch(err=> console.log(err))

上述代码中使用success来模拟异步操作中请求成功的标志,当successtrue时调用resolve 函数,当为 false 时调用 reject 函数。当执行器中执行resolve函数时则调用then函数输出:请求成功,同理当执行器中执行reject函数则输出:请求失败。

Promise的状态

从上面的案例中当执行器执行成功时候则会调用resolve方法进而再次调用then方法,当执行失败的时候则会触发reject方法的执行,在接着执行catch方法,Promise对象通过调用其内部的状态来决定后续操作时如何进行的,其主要有三种状态:

  • pending(等待态)
  • fulfilled(执行态)
  • rejected(拒绝态)

在创建 Promise对象的实例时, Promise的状态默认为挂起(pending),等待操作完成,就可以使用执行器的resolve(Fulfilled)与reject(Rejected) 函数来调整状态,则执行then否则执行catch,查看两个方法的源码都可以看到注释中有这么一段话:Promise for the completion of the callback

interface Promise<T> {
    
    
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled The callback to execute when the Promise is resolved.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of which ever callback is executed.
     */
    then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;

    /**
     * Attaches a callback for only the rejection of the Promise.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of the callback.
     */
    catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}

也就是说无论执行器最后执行的是then或者reject都会重新返回一个新的Promise对象,这样以来就可以使用Promise实现链式调用。
在这里插入图片描述
上面流程图则是Promise对象三种状态的转换图,总结其流程如下:

  • 初始化 Promise 状态(pending
  • 立即执行 Promise 中编程逻辑,将Promise 内部 resolve、reject 函数作为参数,可以执行成功则将状态由pending 改为fulfilled,执行失败则将状态改成reject,此状态转换过程是不可逆的。
  • 然后执行 then(…) 返回一个新的Promise对象然后可以继续调用then(then 方法可被同一个 promise 调用多次)

then方法

方法 then 用于实现 Promise 对象的上一个操作的后续处理。它可接受两个参数,第一个参数用于指定状态为fulfilled时要执行的函数,第二个参数用于指定状态为rejected时要执行的函数,上面查看源码得知then方法的两个参数都可以为undefined。无论是如fulfilled的回调函数,还是 rejected 的回调函数,都具有一个参数,该参数的值为上一波操作传递给resolvereject的值,下面从一个面试题感受一下Promise的使用:

    new Promise((resolve, reject) => {
    
    
      let a = 0;
      setTimeout(() => {
    
    
        resolve(a+1)
        resolve(a+2)
        reject(a+3)
      }, 1000)
    }).then((res1) => {
    
    
      console.log('res1', res1)
    },(res2)=>{
    
    
      console.log('res2',res2)
    }).then((res3) => {
    
    
      console.log('res3', res3)
    })
  }

在这里插入图片描述
请仔细思考一下为何输出上述结果?
当执行完 resolve(a+1) 后 Promise对象状态已从pending 转为fulfilled(执行成功) ,故第一个then方法中第一个参数中的函数会被执行,第二个参数函数只有Promise对象状态已从pending 转为rejected(执行失败)才会执行。而这个then方法又没有通过resolvereject进行参数的传递,所以上面代码执行结果为:
在这里插入图片描述
下面我们将代码做如下变动:
在这里插入图片描述
再次运行程序输出结果如下:
在这里插入图片描述

catch方法

catch方法类似于 then方法,不定义状态为fulfilled时的回调函数,主要用于 rejected状态时的错误处理。使用时,需要传递用于处理错误的回调函数给它 它也会返回 Promise ,因而同样可以使用链式操作的写法。

    new Promise((resolve,reject)=> {
    
    
      // 错误代码
      let success = false
      if(!success) {
    
    
        reject(success)
      } else {
    
    
        resolve(!success)
      }
    }).then(res=> console.log(res))
        .catch(err=> console.log(err))

all方法

做过Java开发的程序员都知道一个类CountDownLatch,它能够使一个线程在等待另外一些线程完成各自工作之后再继续执行。es6中如果有需要执行多个任务都成功后才能执行后续操作的需求 ,就可以使用 all 方法来组合这些任务。

  • 定义promise1对象
    let promise1 = new Promise((resolve,reject)=> {
    
    
      let flag = true
      if(flag) {
    
    
        resolve('promise1执行成功')
      } else {
    
    
        reject('promise1执行失败')
      }
    }).then( res => {
    
    
      console.log(res)
      return Promise.resolve(res)
    }).catch( err => {
    
    
      console.log(err)
      return Promise.reject(err)
    })
  • 定义promise2对象
    let promise2 = new Promise((resolve,reject)=> {
    
    
      let flag =true
      if(flag) {
    
    
        resolve('promise2执行成功')
      } else {
    
    
        reject('promise2执行失败')
      }
    }).then( res => {
    
    
      console.log(res)
      return Promise.resolve(res)
    }).catch( err => {
    
    
      console.log(err)
      return Promise.reject(err)
    })

将以上两个对象加入all方法中进行执行

    let promiseAll =Promise
        .all([promise1, promise2])
        .then(function(results){
    
    
          console.log('全体执行成功,结果:'+results);
        }).catch(err => {
    
    
      console.log('全体执行失败,结果:'+err);
    });

运行页面输出结果:
在这里插入图片描述

以上代码将 promise1 promise2 两个对象组合在一起,只有当两者执行都是成功时(fulfilled), promiseAll的状态才为成功(fulfilled),只要有一个任务失败(·rejected·)则整体都为失败,我们可以让 promise1返回失败(rejected)
在这里插入图片描述
再次运行程序结果如下:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/javaee_gao/article/details/118300094
今日推荐