前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JS原生引用类型解析7-Promise类型

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

作者头像
love丁酥酥
发布2018-08-27 15:42:20
1.3K0
发布2018-08-27 15:42:20
举报
文章被收录于专栏:coding for lovecoding for love

1. 简介

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

2. Promise对象创建

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

代码语言:javascript
复制
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对象传出的值作为参数。

代码语言:javascript
复制
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构造函数的所有属性与方法。

代码语言:javascript
复制
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译者注)

代码语言:javascript
复制
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对象。

代码语言:javascript
复制
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对调试和选择性错误捕捉很有帮助。

代码语言:javascript
复制
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方法

基本用法示例:

代码语言:javascript
复制
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原型对象的所有属性与方法。

代码语言:javascript
复制
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。

语法:

代码语言:javascript
复制
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 的终态作为它的终态。

用法示例:

代码语言:javascript
复制
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) 相同。 语法:

代码语言:javascript
复制
p.catch(onRejected);

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

参数:

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

返回值: 一个Promise。

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

  1. 使用链式语句的 catch方法:
代码语言:javascript
复制
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. 捕获抛出的错误
代码语言:javascript
复制
// 抛出一个错误,大多数时候将调用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. 如果已决议
代码语言:javascript
复制
//创建一个新的 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效果取消。

代码语言:javascript
复制
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实例对象的所有属性与方法。
代码语言:javascript
复制
var p = new Promise(function(resolve, reject) {
    resolve('success');
})
Object.getOwnPropertyNames(p);  // []

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

参考

MDN-Promise Promise 对象

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.03.11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 简介
  • 2. Promise对象创建
  • 3. Promise构造函数的属性与方法
    • 3.1 Promise构造函数的属性
      • 3.2 Promise构造函数的方法
      • 4. Promise原型对象的属性与方法
        • 4.1 Promise原型对象的属性
          • 4.2 Promise原型对象的方法
          • 参考
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档