最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • ES6 Promise的基本使用

    正文概述 掘金(廊坊吴彦祖)   2020-12-10   651

    什么是 Promise

    Promise 是异步编程的一种解决方案,比传统的解决方案:回调函数和事件,更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象

    Promise 对象两个特点:

    • 1、对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:

      • pending:(进行中)初始状态,没有变为成功或失败
      • fulfilled:(已成功)意味着操作成功完成
      • rejected:(已失败)意味着操作失败

      一个新创建的 Promise 处于 pending 状态,当调用 resolve 或 reject 函数后,Promise 处于 fulfilled 或 rejected 状态,此后 Promise 的状态保持不变 只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 的英语意思「承诺」的由来,表示其他手段无法改变

    • 2、一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变只有两种可能:从 pending 变为 fulfilled 或从 pending 变为 rejected。只要这两种情况发生,状态就不会再改变,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的

    Promise 缺点:

    • 无法取消 Promise,一旦新建它就会立即执行,中途无法取消
    • 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部
    • 当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

    Promise.resolve() 和 Promise.reject() 方法

    使用 new 来调用 Promise 的构造函数创建 Promise 实例(Promise 实例对象被创建后就会立即执行),该构造函数会把一个叫做“处理器函数”(executor function)的函数作参数。这个“处理器函数”接受两个函数参数: resolvereject 。当异步任务顺利完成且返回结果值时,会调用 resolve 函数,当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用 reject 函数

    let p = new Promise(function(resolve, reject) {
        //执行一些异步任务
        if(/* 异步操作成功 */) {
        	resolve(res)   //调用resolve函数,pending状态 -> fulfilled状态,将异步操作的结果,作为参数传递出去
        }else {
        	reject(err)   //调用reject函数,pending状态 -> rejected状态,将异步操作报出的错误,作为参数传递出去
    	}
    })
    

    Promise.prototype.then() 链式调用

    then() 方法为 Promise 实例添加状态改变时的回调函数,该方法返回一个新的 Promise 实例,因此可以采用 then() 方法后面再调用另一个 then() 方法的链式写法 该方法可以接受两个回调函数作为参数:

    • 第一个参数是 fulfilled 状态的回调函数
    • 第二个参数(可选)是 rejected 状态的回调函数(两个函数都接受 Promise 对象传出的值作为参数)

    then() 方法链式调用时,前一个 then() 方法指定的回调函数,返回一个新的 Promise 实例,后一个 then() 方法指定的回调函数,会等待这个新的 Promise 实例状态发生变化后,调用对应状态的回调函数

    let p = new Promise(function(resolve, reject) {
        //执行一些异步任务
        if(/* 异步操作成功 */) {
        	resolve(res)   //调用resolve函数,pending状态 -> fulfilled状态,将异步操作的结果,作为参数传递出去
        }else {
        	reject(err)   //调用reject函数,pending状态 -> rejected状态,将异步操作报出的错误,作为参数传递出去
    	}  
    })
    p.then(function(res) {
    	console.log(res)   //fulfilled状态的回调函数,接收resolve函数的值作为参数
    }, function(err) {
    	console.log(err)   //rejected状态的回调函数,接收reject函数的值作为参数
    })
    

    Promise.prototype.catch() 捕获异常

    catch() 方法用于指定发生错误时的回调函数。如果 Promise 实例中异步操作抛出错误,状态变为 rejected,就会被 catch() 方法捕获到这个错误(在 resolve 函数后面抛出错误,不会被捕获到,此时 Promise 对象的状态已经改变为 fulfilled,不会再改变),调用 catch() 方法指定的回调函数做错误的处理。如果 then() 方法指定的回调函数,在运行中抛出错误,也会被 catch() 方法捕获到 该方法是 then(null, rejection) 或 then(undefined, rejection) 的别名(建议使用 catch() 方法,而不使用 then() 方法的第二个回调函数参数,因为 catch() 方法可以捕获到 then() 方法运行中抛出的错误)

    let p = new Promise(function(resolve, reject) {
        //执行一些异步任务,pending状态改变时执行对应的函数
        if(/* 异步操作成功 */) {
        	resolve(res)   //pending状态 -> fulfilled状态,将异步操作的结果,作为参数传递出去
        }else {
        	reject(err)   //pending状态 -> rejected状态,将异步操作报出的错误,作为参数传递出去
    	}   
    })
    p.then(function(res) {
    	console.log(res)   //fulfilled状态的回调函数,接收resolve函数的值作为参数
    }).catch(function(err) {
    	console.log(err)   //rejected状态的回调函数,接收reject函数的值作为参数(或者then()方法在运行中抛出的错误)
    })
    

    解决回调地狱的问题

    JavaScript 本身是单线程的,为了解决一些单线程带来的问题,异步编程成为了 JavaScript 中非常重要的一部分。当我们使用多次异步调用,异步调用的结果如果存在依赖,则会形成回调函数的嵌套,当回调函数嵌套层级过多时,就形成了回调地狱问题,回调地狱会使代码变得可读性差、难以理解和维护

    let num = 1
    let addNum = function(callback) {
        setTimeout(function(){
            num++
    		callback()
    	}, 1000)
    }
    //回调函数嵌套层级过多时,就形成了回调地狱问题,使代码变得可读性差、难以理解和维护
    addNum(function() {
        addNum(function() {
            addNum(function() {
                addNum(function() {
                    console.log(num)   //5
                })
            })
        })
    })
    

    Promise 很好的解决了回调地狱的问题,提高代码的可读性和可维护性。Promise 采用链式的 then(),可以指定一组按照次序调用的回调函数,前一个回调函数返回一个 Promise 对象(即有异步操作),后一个回调函数就会等待该 Promise 对象的状态发生变化,才会被调用 前一个回调函数完成后,会将返回结果作为参数,传入第二个回调函数

    let num = 1
    let addNum = function(num) {
        return new Promise(function(resolve, reject) {
            setTimeout(function(){
                num++
                resolve(num)
            }, 1000)
        })
    }
    //Promise采用链式的then()解决回调地狱问题,提高了代码的可读性和可维护性
    addNum(num).then(function(res) {
        return addNum(res)
    }).then(function(res) {
        return addNum(res)
    }).then(function(res) {
        return addNum(res)
    }).then(function(res) {
        console.log(res)   //5
    }).catch(function(err) {
        console.log(err)
    })
    

    Promise.all()

    Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。该方法接受一个数组作为参数,数组的每一项都应为 Promise 实例,如果不是,就会先调用 resolve() 方法,将参数转为 Promise 实例,再进一步处理(该方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例) Promise.all() 方法包装后的新 Promise 实例的状态由数组参数的每一项决定:

    • 只有作为参数的 Promise 实例的状态都变成 fulfilled,包装后的新 Promise 实例的状态才会变成 fulfilled,此时所有作为参数的 Promise 实例的返回值组成一个数组,传递给包装后的新 Promise 实例的回调函数

      let p1 = new Promise(function(resolve, reject) {
          setTimeout(function() {
              resolve(1)
          }, 1000)
      })
      let p2 = new Promise(function(resolve, reject) {
          setTimeout(function() {
              resolve(2)
          }, 2000)
      })
      //作为参数的Promise实例的状态都变成fulfilled
      //then()接受到作为参数的Promise实例的返回值的数组
      Promise.all([p1, p2]).then(function(res) {
          console.log(res)   //[1, 2]
      }).catch(function(err) {
          console.log(err)
      })
      
    • 只要作为参数的 Promise 实例中有一个状态变为 rejected,包装后的新 Promise 实例的状态就会变成 rejected,此时第一个状态变为 rejected 的实例的返回值,会传递给包装后的新 Promise 实例的回调函数(注意:如果作为参数的 Promise 实例,自己定义了catch() 方法,一旦它变为 rejected 状态,并不会被 Promise.all() 的 catch() 方法捕获)

      let p1 = new Promise(function(resolve, reject) {
          setTimeout(function() {
              resolve(1)
          }, 1000)
      })
      let p2 = new Promise(function(resolve, reject) {
          setTimeout(function() {
              reject("error")
          }, 2000)
      })
      //作为参数的Promise实例中有一个状态变为rejected
      //catch()捕获到第一个状态变为rejected的实例的返回值
      Promise.all([p1, p2]).then(function(res) {
          console.log(res)  
      }).catch(function(err) {
          console.log(err)   //"error"
      })
      
    总结:全部成功,得到成功的数组;只要有一个失败,得到第一个失败

    Promise.race()

    Promise.race() 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。该方法和 Promise.all() 方法一样,接受一个数组作为参数,数组的每一项都应为 Promise 实例,如果不是,就会先调用 resolve() 方法,将参数转为 Promise 实例,再进一步处理 Promise.race() 方法包装后的新 Promise 实例的状态:

    • 只要作为参数的 Promise 实例中有一个实例率先改变状态,包装后的新 Promise 实例的状态就跟着改变,那个率先改变的 Promise 实例的返回值,会传递给包装后的新 Promise 实例的回调函数

      let p1 = new Promise(function(resolve, reject) {
          setTimeout(function() {
              resolve(1)
          }, 1000)
      })
      let p2 = new Promise(function(resolve, reject) {
          setTimeout(function() {
              reject("error")
          }, 2000)
      })
      //作为参数的Promise实例中有一个实例率先改变状态为fulfilled
      //then()接受到那个率先改变状态为fulfilled的Promise实例的返回值
      Promise.race([p1, p2]).then(function(res) {
          console.log(res)   //1
      }).catch(function(err) {
          console.log(err)
      })
      
      let p1 = new Promise(function(resolve, reject) {
          setTimeout(function() {
              reject("error")
          }, 1000)
      })
      let p2 = new Promise(function(resolve, reject) {
          setTimeout(function() {
              resolve(2)
          }, 2000)
      })
      //作为参数的Promise实例中有一个实例率先改变状态为rejected
      //catch()捕获到那个率先改变状态为rejected的Promise实例的返回值
      Promise.race([p1, p2]).then(function(res) {
          console.log(res)  
      }).catch(function(err) {
          console.log(err)   //"error"  
      })
      
    总结:得到最先结束的状态,不管是成功还是失败

    起源地下载网 » ES6 Promise的基本使用

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元