Promise使用须知

作者: 狄恩莫里亚蒂 | 来源:发表于2017-03-03 16:01 被阅读2100次

    一.关于Promise
    promise 是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise
    对象。
    所谓Promise
    ,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
    它代表一个异步操作。有三种状态:
    pending
    (进行中): 初始状态, 初始状态,未完成或拒绝。
    resolved
    (已完成): 意味着操作成功完成。又名fulfilled。
    rejected
    (已失败): 意味着操作失败。

    有了Promise
    ,就可以将异步操作以同步操作的流程表达出,但它也有如下缺点
    一旦创建它就会立即执行,无法中途取消。

    如果不设置回调函数,Promise
    内部抛出的错误,不会反应到外部。

    当处于Pending
    状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

    二.应用示例

    1. 创建Promise
    var promise = new Promise(
    
       /* executor */ 
       function(resolve, reject){
        ...
       }
    );
    

    ​ executor函数由Promise实现立即执行,传递resolve和reject函数. (在Promise构造函数之前调用执行器甚至返回创建的对象)
    ​ 在 executor内部,promise有如下的状态变化可能:
    ​ 1. resolve被调用,promise由pending变为resolved,代表该Promise被成功解析(resolve)
    ​ 2.reject被调用,promise由pending变为rejected,代表该Promise的值不能用于后续处理,即被拒绝了
    注意:
    如果在executor方法的执行过程中抛出了任何异常,那么promise立即被拒绝(即相当于reject方法被调用),executor的返回值也就会被忽略。 如果一个promise对象处在resolved或rejected状态而不是pending状态,那么它也可以被称为settled状态。

    2.处理Promise实例
    then:Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数。

    promise.then(function(value) {
    
     // success 状态为Resolved时调用
    }, function(error) { 
     // failure 状态为Reject时调用
    });
    

    then方法可以接受两个回调函数作为参数。
    第一个回调函数:在promise的状态变成resolved时被调用。它的参数promise的resolve方法的返回值。
    第二个回调函数:在promise的状态变成rejected时被调用。它的参数promise的reject方法的返回值。且为可选参数。
    catch:Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数

    promise.then(function(posts) {
    
     // ...
    }).catch(function(error) {
     console.log('发生错误!', error); // 处理 promise 和 前一个回调函数运行时发生的错误
    });
    

    扩展:
    Promise.prototype.then
    和 Promise.prototype.catch
    方法返回 promises对象, 所以它们可以被链式调用。但此时返回的是以函数返回值生成的新的Promise实例,不是原来那个Promise实例。[图片上传中。。。(1)]

    注意问题:
    Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

    3.将多个Promise实例,包装成一个新的Promise实例
    **Promise.all(iterable)
    ** :当所有在可迭代参数中的 promises 已完成,或者第一个传递的 promise(指 reject)失败时,返回 promise。

    var p = Promise.all([p1, p2, p3]);

    当p1、p2和p3的状态均为resolved时p的状态为resolved。

    当p1、p2或p3中的任意一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
    具体的使用示例如下:
    情形一:全部成功的情况

    Promise.all([Promise.resolve('foo'),Promise.resolve('bar'),Promise.resolve('test')])
    
     .then(function(result){
               console.log(result); //结果为:[foo,bar,test]。即所有返回值的数组。
    
       })
       .catch(function(error) {
         console.log('error');   //不会被执行
       });
    

    情形二:全部失败或部分失败

    Promise.all([Promise.resolve('foo'),Promise.reject('barError'),Promise.reject('testError')])
    
       .then(function(result){
               console.log(result);           //成功回调 不会被执行
    
       })
    
       .catch(function(error) {
         console.log(error);   //结果为barError或testError。即第一个被reject的值。
    
       });
    

    三.常见的使用误区
    忘记添加 .catch()
    通常情况,promise有如下两种处理方式:

    // ------------   不好的写法   -------------
    
    promise.then(function(data) {
       // success
     }, function(err) {   //仅处理promise运行时发生的错误。无法处理回调中的错误
       // error
     });
    
    // ------------   好的写法   ------------
    promise.then(function(data) {
       // success
    }).catch(function(err) {   // 处理 promise 和 前一个回调函数运行时发生的错误
       // error 
    });
    

    因为promise抛出的错误不会传递到外层代码。当使用没有catch的第一种种写法时,成功回调的错误将无法被处理。因此比较好的方式是,总是使用catch方法。

    在then或者catch函数中不使用return下面是在是用Promise时,一个很常见的错误写法:

    //------------   不好的写法   ------------------
    
    promise.then(function () {
      getUserInfo(userId);
    }).then(function () {
     // 在这里可能希望在这个回调中使用用户信息,但你可能会发现它根本不存在
    });
    

    会发生上面的错误,是因为对Promise的返回及链式调用的理解不够。
    每一个promise都会给你一个then()方法(或者catch,它们只是then(null,…)的语法糖)。这里我们是在then()方法的内部来看:

    promise.then(function () {
    
      return getUserInfo(userId);
    }).then(function (userInfo) {
       HadleUser(userInfo);
    }).catch(function(error) {
       console.log(error);
    });
    

    在回调中在有三种事可以做:
    返回另一个promise
    如果getUserInfo返回一个Promise,则HadleUser将在该promise变为resolved后执行,并以该promise的返回作为入参。
    返回一个同步值(或者undefined)
    getUserInfo返回一个同步值,则改值会被包装成一个resolved状态的promise,HadleUser
    将被立刻执行,并以getUserInfo返回作为入参。
    抛出一个同步错误
    getUserInfo返回一个同步错误,则该错误会被包装成一个rejected状态的promise,最终在catch中被捕获。此时HadleUser将不会被执行。
    promises丢失
    你认为下面的代码会打印出什么?

    Promise.resolve('foo').then(Promise.resolve('bar')).then(function (result) {
      console.log(result);
    });
    

    如果你认为打印出bar,那你就大错特错了。它实际上会打印出foo。
    原因是当你给then()传递一个非函数(比如一个promise)值的时候,它实际上会解释为then(null),这会导致之前的promise的结果丢失。

    四.最佳实践总结

    • then方法中 永远 return或 throw
    • 如果 promise链中可能出现错误,一定添加 catch
    • 永远传递函数给 then方法
    • 不要把 promise写成嵌套

    参考链接:
    Promise -JavaScript | MDN
    Promises 很酷,但很多人并没有理解就在用了
    Promise 对象

    相关文章

      网友评论

        本文标题:Promise使用须知

        本文链接:https://www.haomeiwen.com/subject/pwkrgttx.html