美文网首页
JS原生引用类型解析7-Promise类型

JS原生引用类型解析7-Promise类型

作者: love丁酥酥 | 来源:发表于2018-03-11 12:46 被阅读26次

    1. 简介

    ES6引入了一个全新的对象Promise,用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。Promise最直接的好处就是链式调用,另外在错误捕获上也很方便。用同步的写法解决异步问题,代码直观,易于理解维护,解决了回调地狱的问题。关于Promise的详细讲解和更多用例我会开专门文章讨论。这里我们主要看一下Promise及其原型的属性和方法。

    2. Promise对象创建

    Promise对象使用new构造函数创建。基本使用方法如下:

    const promise = new Promise(function(resolve, reject) {
      // ... some code
    
      if (/* 异步操作成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });
    

    Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

    then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。

    var promise = new Promise(function(resolve, reject) {
      setTimeout(resolve, 1000, 'foo');
    });
    promise.then(function(val){
        console.log(val);  // 1000ms后输出 ‘foo’
    })
    

    3. Promise构造函数的属性与方法

    我们用Object.getOwnPropertyNames()方法获取Promise构造函数的所有属性与方法。

    Object.getOwnPropertyNames(Promise);
    // (7) ["length", "name", "prototype", "all", "race", "resolve", "reject"]
    

    发现一共有7个属性和方法。

    3.1 Promise构造函数的属性

    Promise.length
    长度总为1 (构造器参数的数目)

    Promise.name
    名称为"Promise"

    Promise.prototype
    指向Promise构造函数的原型,可以为所有 Promise 类型的对象添加属性。

    3.2 Promise构造函数的方法

    Promise.all(iterable)
    这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。这个新的promise对象在触发成功状态以后,会把一个包含iterable里所有promise返回值的数组作为成功回调的返回值,顺序跟iterable的顺序保持一致;如果这个新的promise对象触发了失败状态,它会把iterable里第一个触发失败的promise对象的错误信息作为它的失败错误信息。Promise.all方法常被用于处理多个promise对象的状态集合。(可以参考jQuery.when方法---MDN Promise译者注)

    var promise1 = Promise.resolve(3);
    var promise2 = 42;
    var promise3 = new Promise(function(resolve, reject) {
      setTimeout(resolve, 100, 'foo');
    });
    
    Promise.all([promise1, promise2, promise3]).then(function(values) {
      console.log(values);
    });
    // expected output: Array [3, 42, "foo"]
    

    当然,当参数不包含 Promise 时, 该方法返回完成(resolve),但这显然没有什么意义。

    Promise.race(iterable)
    当iterable参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。

    var promise1 = new Promise(function(resolve, reject) {
        setTimeout(resolve, 500, 'one');
    });
    
    var promise2 = new Promise(function(resolve, reject) {
        setTimeout(resolve, 100, 'two');
    });
    
    Promise.race([promise1, promise2]).then(function(value) {
      console.log(value);
      // Both resolve, but promise2 is faster
    });
    // expected output: "two"
    

    Promise.reject(reason)
    返回一个状态为失败的Promise对象,并将给定的失败信息reason(Promise被拒绝的原因)传递给对应的处理方法。此处使用Error实例的reason对调试和选择性错误捕捉很有帮助。

    Promise.reject("Testing static reject").then(function(reason) {
      // 未被调用
    }, function(reason) {
      console.log(reason); // "Testing static reject"
    });
    
    Promise.reject(new Error("fail")).then(function(error) {
      // 未被调用
    }, function(error) {
      console.log(error); // 堆栈跟踪
      /* Error: fail
        at <anonymous>:7:16 */
    });
    

    Promise.resolve(value)
    返回一个状态由给定value决定的Promise对象。如果该值是一个Promise对象,则直接返回该对象;如果该值是thenable(即,带有then方法的对象),返回的Promise对象的最终状态由then方法执行决定;否则的话(该value为空,基本类型或者不带then方法的对象),返回的Promise对象状态为fulfilled,并且将该value传递给对应的then方法。通常而言,如果你不知道一个值是否是Promise对象,使用Promise.resolve(value) 来返回一个Promise对象,这样就能将该value以Promise对象形式使用。

    用法如下:

    1. Promise.resolve(promise);
      直接返回该对象promise

    2. Promise.resolve(thenable);
      返回一个最终状态由then方法执行决定的Promise对象

    3. Promise.resolve(value)
      value为空,基本类型,或者不带then方法的对象,返回状态为fulfilled的Promise对象,并且将该value传递给对应的then方法

    基本用法示例:

    var promise1 = Promise.resolve([1, 2, 3]);
    
    promise1.then(function(value) {
      console.log(value);
      // expected output: Array [1, 2, 3]
    });
    

    4. Promise原型对象的属性与方法

    我们用Object.getOwnPropertyNames()方法获取Promise原型对象的所有属性与方法。

    Object.getOwnPropertyNames(Promise.prototype);
    // (4) ["constructor", "then", "catch", "finally"]
    

    发现一共有4个属性和方法。

    4.1 Promise原型对象的属性

    Promiset.prototype.constructor
    指向构造函数Promise

    4.2 Promise原型对象的方法

    Promise.prototype.then(onFullfilled, onRejected)
    它最多需要有两个参数:Promise 的接受(fulfillment)和拒绝(rejection)情况的回调函数。返回一个新的 Promise,该Promise将以回调的返回值来resolve。

    语法:

    p.then(onFulfilled, onRejected);
    
    p.then(function(value) {
       // fulfillment
      }, function(reason) {
      // rejection
    });
    

    参数:

    • onFulfilled
      当Promise变成接受状态(fulfillment)时,该参数作为回调函数被调用。该函数有一个参数,即接受的值(the fulfillment value)。
    • onRejected
      当Promise变成拒绝状态(rejection )时,该参数作为回调函数被调用。该函数有一个参数,即拒绝的原因(the rejection reason)。

    返回值:

    then方法返回一个Promise。而它的行为与then中的回调函数的返回值有关:

    • 如果then中的回调函数返回一个值,那么then返回的Promise将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。

    • 如果then中的回调函数抛出一个错误,那么then返回的Promise将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。

    • 如果then中的回调函数返回一个已经是接受状态的Promise,那么then返回的Promise也会成为接受状态,并且将那个Promise的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。

    • 如果then中的回调函数返回一个已经是拒绝状态的Promise,那么then返回的Promise也会成为拒绝状态,并且将那个Promise的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。

    • 如果then中的回调函数返回一个未定状态(pending)的Promise,那么then返回Promise的状态也是未定的,并且它的终态与那个Promise的终态相同;同时,它变为终态时调用的回调函数参数与那个Promise变为终态时的回调函数的参数是相同的。

    注意:

    如果忽略针对某个状态的回调函数参数,或者提供非函数 (nonfunction) 参数,那么 then 方法将会丢失关于该状态的回调函数信息,但是并不会产生错误。如果调用 then 的 Promise 的状态(fulfillment 或 rejection)发生改变,但是 then 中并没有关于这种状态的回调函数,那么 then 将创建一个没有经过回调函数处理的新 Promise 对象,这个新 Promise 只是简单地接受调用这个 then 的原 Promise 的终态作为它的终态。

    用法示例:

    var promise1 = new Promise(function(resolve, reject) {
      resolve('Success!');
    });
    
    promise1.then(function(value) {
      console.log(value);
      // expected output: "Success!"
    });
    

    Promise.catch(onRejected)
    添加一个拒绝(rejection) 回调到当前 promise, 返回一个新的promise。当这个回调函数被调用,新 promise 将以它的返回值来resolve。它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。
    语法:

    p.catch(onRejected);
    
    p.catch(function(reason) {
       // 拒绝
    });
    

    参数:

    • onRejected
      当Promise 被拒绝时,被调用的一个Function。该函数拥有一个参数:
    • reason
      拒绝的原因。

    返回值:
    一个Promise。

    示例:
    有三种常见的使用情况:

    1. 使用链式语句的 catch方法:
    var p1 = new Promise(function(resolve, reject) {
      resolve('Success');
    });
    
    p1.then(function(value) {
      console.log(value); // "成功!"
      throw 'oh, no!';
    }).catch(function(e) {
      console.log(e); // "oh, no!"
    }).then(function(){
      console.log('after a catch the chain is restored');
    }, function () {
      console.log('Not fired due to the catch');
    });
    
    // 以下行为与上述相同
    p1.then(function(value) {
      console.log(value); // "成功!"
      return Promise.reject('oh, no!');
    }).catch(function(e) {
      console.log(e); // "oh, no!"
    }).then(function(){
      console.log('after a catch the chain is restored');
    }, function () {
      console.log('Not fired due to the catch');
    });
    
    1. 捕获抛出的错误
    // 抛出一个错误,大多数时候将调用catch方法
    var p1 = new Promise(function(resolve, reject) {
      throw 'Uh-oh!';
    });
    
    p1.catch(function(e) {
      console.log(e); // "Uh-oh!"
    });
    
    // 在异步函数中抛出的错误不会被catch捕获到
    var p2 = new Promise(function(resolve, reject) {
      setTimeout(function() {
        throw 'Uncaught Exception!';
      }, 1000);
    });
    
    p2.catch(function(e) {
      console.log(e); // 不会执行
    });
    
    // 在resolve()后面抛出的错误会被忽略
    var p3 = new Promise(function(resolve, reject) {
      resolve();
      throw 'Silenced Exception!';
    });
    
    p3.catch(function(e) {
       console.log(e); // 不会执行
    });
    
    1. 如果已决议
    //创建一个新的 Promise ,且已决议
    var p1 = Promise.resolve("calling next");
    
    var p2 = p1.catch(function (reason) {
        //这个方法永远不会调用
        console.log("catch p1!");
        console.log(reason);
    });
    
    p2.then(function (value) {
        console.log("next promise's onFulfilled"); /* next promise's onFulfilled */
        console.log(value); /* calling next */
    }, function (reason) {
        console.log("next promise's onRejected");
        console.log(reason);
    });
    

    Promise.prototype.finally(onFinally)
    添加一个事件处理回调于当前promise对象,并且在原promise对象解析完毕后,返回一个新的promise对象。回调会在当前promise运行完毕后被调用,无论当前promise的状态是完成(fulfilled)还是失败(rejected)

    注意:
    finally() 虽然与 .then(onFinally, onFinally) 类似,它们不同的是:

    • 调用内联函数时,不需要多次声明该函数或为该函数创建一个变量保存它。
      由于无法知道promise的最终状态,所以finally的回调函数中不接收任何参数,它仅用于无论最终结果如何都要执行的情况。
    • 与Promise.resolve(2).then(() => {}, () => {}) (resolved的结果为undefined)不同,Promise.resolve(2).finally(() => {}) resolved的结果为 2。
    • 同样,Promise.reject(3).then(() => {}, () => {}) (resolved 的结果为undefined), Promise.reject(3).finally(() => {}) rejected 的结果为 3。

    用法示例:
    一个典型的用法,在发出请求时,页面的loading效果开启,然后不管返回的结果是完成(fulfilled)还是失败(rejected),都会执行onFinally将loading效果取消。

    let isLoading = true;
    
    fetch(myRequest).then(function(response) {
        var contentType = response.headers.get("content-type");
        if(contentType && contentType.includes("application/json")) {
          return response.json();
        }
        throw new TypeError("Oops, we haven't got JSON!");
      })
      .then(function(json) { /* process your JSON further */ })
      .catch(function(error) { console.log(error); })
      .finally(function() { isLoading = false; });
    
    1. Promise实例对象的属性与方法
      我们用Object.getOwnPropertyNames()方法获取Promise实例对象的所有属性与方法。
    var p = new Promise(function(resolve, reject) {
        resolve('success');
    })
    Object.getOwnPropertyNames(p);  // []
    

    我们发现实例本身没有绑定属性与方法。

    参考

    MDN-Promise
    Promise 对象

    相关文章

      网友评论

          本文标题:JS原生引用类型解析7-Promise类型

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