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

    正文概述 掘金(没故事的燕同学)   2020-12-30   407

    今天知乎上有网友提问—— 你对 Promise 的了解?

    一、Promise 之前的时代——回调时代

    假设我们用 getUser 来说去用户数据,它接收两个回调 sucessCallback 和 errorCallback:

    function getUser(successCallback, errorCallback){
      $.ajax({
        url:'/user',
        success: function(response){
          successCallback(response)
        },
        error: function(xhr){
          errorCallback(xhr)  
        }
      })
    }
    

    看起来还不算复杂。

    如果我们获取用户数据之后还要获取分组数组、分组详情等,代码就会是这样:

    getUser(function(response){
        getGroup(response.id, function(group){
          getDetails(groupd.id, function(details){
            console.log(details)
          },function(){
            alert('获取分组详情失败')
          })
        }, function(){
          alert('获取分组失败')
        })
      }, function(){
      alert('获取用户信息失败')
    })
    

    三层回调,如果再多一点嵌套,就是「回调地狱」了。

    二、Promise 来了

    Promise 的思路呢,就是 getUser 返回一个对象,你往这个对象上挂回调:

    var promise = getUser()
    promise.then(successCallback, errorCallback)
    

    当用户信息加载完毕,successCallback 和 errorCallback 之一就会被执行。

    把上面两句话合并成一句就是这样的:

    getUser().then(successCallback, errorCallback)
    

    如果你想在用户信息获取结束后做更多事,可以继续 .then:

    getUser().then(success1).then(success2).then(success3)
    

    请求成功后,会依次执行 success1、success2 和 success3。

    如果要获取分组信息:

    getUser().then(function(response){
      getGroup(response.id).then(function(group){
        getDetails(group.id).then(function(){
          
        },error3)
      },error2)
    }, error1)
    

    这种 Promise 写法跟前面的回调看起来其实变化不大。

    真的,Promise 并不能消灭回调地狱,但是它可以使回调变得可控。你对比下面两个写法就知道了。

    getGroup(response.id, success2, error2)
    
    getGroup(response.id).then(success2, error2)
    

    用 Promise 之前,你不能确定 success2 是第几个参数;

    用 Promise 之后,所有的回调都是

    .then(success, error) 
    

    这样的形式。

    三、Promise 的用途

    总结一下上面说的内容:Promise 对象是 JavaScript 的异步操作解决方案,为异步操作提供统一接口。它起到代理作用(proxy),充当异步操作与回调函数之间的中介,使得异步操作具备同步操作的接口。Promise 可以让异步操作写起来,就像在写同步操作的流程,而不必一层层地嵌套回调函数。

    四、如何创建一个 new Promise

    • return new Promise((resolve,reject)=>{...})
    • 任务成功调用resolve(result)
    • 任务失败调用reject(error)
    • resolve和reject 会再去调用成功和失败函数
    • 使用 .then(success,fail)传入成功和失败函数

    下面演示使用方法:

    var promise = new Promise(function (resolve, reject) {
      // ...
    
      if (/* 异步操作成功 */){
        resolve(value);
      } else { /* 异步操作失败 */
        reject(new Error());
      }
    });
    

    上面代码中,Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由 JavaScript 引擎提供,不用自己实现。

    resolve函数的作用是,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。

    reject函数的作用是,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

    五、如何使用 Promise.prototype.then(来源 MDN)

    Promise 实例的then方法,用来添加回调函数。

    then方法可以接受两个回调函数,第一个是异步操作成功时的回调函数,第二个是异步操作失败时的回调函数(该参数可以省略)。一旦状态改变,就调用相应的回调函数。 then方法可以链式使用。

    p1
      .then(step1)
      .then(step2)
      .then(step3)
      .then(
        console.log,
        console.error
      );
    

    上面代码中,p1后面有四个then,意味依次有四个回调函数。只要前一步的状态变为fulfilled,就会依次执行紧跟在后面的回调函数。

    最后一个then方法,回调函数是console.logconsole.error,用法上有一点重要的区别。console.log只显示step3的返回值,而console.error可以显示p1step1step2step3之中任意一个发生的错误。举例来说,如果step1的状态变为rejected,那么step2step3都不会执行了(因为它们是resolved的回调函数)。Promise 开始寻找,接下来第一个为rejected的回调函数,在上面代码中是console.error。这就是说,Promise 对象的报错具有传递性。

    六、如何使用 Promise.all(来源 MDN)

    方法返回一个Promise实例,此实例在 iterable 参数内所有的promise 都完成(resolved)时回调完成(resolve);如果参数中 promise有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败promise的结果。

    Promise2.all = function(arrP) {
      let list = []
      let len = 0
      let hasErr = false
      return new Promise2((resolve, reject) => {
        for(let i = 0; i < arrP.length; i++) {
          arrP[i].then( data=> {
            list[i] = data
            len++
            len === arrP.length && resolve(list)
          }, error => {
            !hasErr && reject(error)
            hasErr = true
          })
        }
      })
    }
    

    七、如何使用 Promise.race(来源 MDN)

    方法返回一个Promise实例,一旦迭代器中的某个 promise 完成(resolved)或失败(rejected),返回的 promise 就会 resolve 或 reject

    Promise2.race = function(arrP) {
      let hasValue = false
      let hasError = false
      return new Promise2((resolve, reject) => {
        for(let i = 0; i < arrP.length; i++) {
          arrP[i].then(data => {
            !hasValue && !hasError && resolve(data) 
            hasValue = true
          }, error => {
            !hasValue && !hasError &&reject(error)
            hasError = true
          })
        }
      })
    }
    

    八、小结

    Promise 的优点在于,让回调函数变成了规范的链式写法,程序流程可以看得很清楚。它有一整套接口,可以实现许多强大的功能,比如同时执行多个异步操作,等到它们的状态都改变以后,再执行一个回调函数;再比如,为多个回调函数中抛出的错误,统一指定处理方法等等。

    而且,Promise 还有一个传统写法没有的好处:它的状态一旦改变,无论何时查询,都能得到这个状态。这意味着,无论何时为 Promise 实例添加回调函数,该函数都能正确执行。所以,你不用担心是否错过了某个事件或信号。如果是传统写法,通过监听事件来执行回调函数,一旦错过了事件,再添加回调函数是不会执行的。

    Promise 的缺点是,编写的难度比传统写法高,而且阅读代码也不是一眼可以看懂。你只会看到一堆then,必须自己在then的回调函数里面理清逻辑。


    起源地下载网 » 你对 Promise 的了解?

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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