ES6学习笔记17:Promise对象

Promise 的含义

Promise 是异步编程的一种解决方案,以传统的解决方案:回调函数和事件更合理和强大。

定义

Promise 是一个对象,从它可以回去一步操作的消息,Promise提供统一的API,各种异步操作都可以用同样的方法进行处理。

特点
  • 对象的状态不受外界的影响,对象的状态有pending(进行中)、fulfilled(成功)、rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  • 一旦状态改变,就不会再变,任何时候都可以的到这个结果。Promise对象的状态改变只有:1从pending变为fulfilled或者从pending变为rejected,只要这两个情况发生了,状态就不会再变了。
优点
  • 有了Promise对象,就可以将一步操作以同步流程表达出来,避免了层层嵌套的回调函数。
  • 提供统一的接口,是的控制异步操作更加容易
缺点
  • 无法取消Promise
  • 内部抛出的错误无法反映到外部
  • 当处于pending状态时,无法知道进行到哪一个阶段

基本用法

ES6中,Promise对象是一个构造函数,用来生成Promise实例

const promise = new Promise(function(resolve, reject){
  // ...
  // ... resolve(value)
  // ... reject(error)
})

构造函数Promise接受一个参数,函数的参数分别是resolvereject。这两个函数由JavaScript引起提供,不用自己部署

函数resolve作用将Promise对象的状态从未完成变为成功,操作成功时调用,并将一步操作的结果作为参数传递出去。

函数reject作用将Promise对象的状态从未完成变为失败,在异步操作失败时调用,并将异步操作的报错作为参数传递出去。

方法then可以接受两个回调函数作为参数,第一个回调函数的是Promise 对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可以选的。

function timeout(ms){
  return new Promise((resolve,reject)=>{
    setTimeout(resolve,ms,'done');
  })
}
timeout(100).then((value)=>{
  console.log(value); // 'done'
})

Promise 新建以后就会立即执行

const promise = new Promise((resolve, reject)=>{
  console.log('Promise');
  resolve();
});

promise.then(()=>{
  console.log('resolve');
});
console.log('abc');
// 输出顺序
// 'Promise'
// 'abc'
// 'resolve'

当调用resolve函数和reject函数带有参数时,那么这些参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出异常错误;resolve函数的参数除了正常值以外,还可能是另一个Promis 实例。

const promise1 = new Promise(function(resolve,reject){
  // ...
})

const promise2 = new Promise(function(resolve,reject){
  // ...
  resolve(p1);
})

上面的代码中,promise1promise2都是 Promise 的实例,但是promise2resolve方法将promise1作为参数,即:一个异步操作的结果是返回另一个异步操作。注意,这个时候的promise1的状态会传递给promise2,promise1的状态决定promise2的状态。当promise1的状态为pending那么promise2的回调函数会等待promise1的状态的改变。当promise1的状态为resolved或者为reject,那么promise2的回调函数会立即执行。

const p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log('p1 is reject');
        reject(new Error('fail'));
      }, 3000);
    });

    const p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log('p2 is resolve');
        resolve(p1);
      }, 1000);
    });

    p2.then(result => console.log(result))
      .catch(error => console.log(error));

// p2 is resolve
// p1 is reject
// Error: fail

上面代码p1p2在创建的时候都是立即执行的,在执行经过1000ms以后,p2输出p2 is resolve然后等待p1的状态,再经过2000ms以后,p1输出p1 is reject,然后p1的状态为reject并抛出异常,这个时候p2再次执行,由于p1抛出异常,所以被p2捕获,此时就输出Error: fail.

  • 调用resolve或者reject并不会终结Promis的参数函数的执行
const promise = new Promise((resolve, reject)=>{
  resolve(1);
  console.log(2);
}).then(value=>{
  console.log(value);
});
// 2
// 1

Promise.prototype.then()

Promise实例具有定义在原型对象上的then方法,then方法为Promise实例添加状态改变时的回调函数。then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数。then方法返回一个新的Promise实例(和其之前不同的Promise实例)。因此可以采用链式写法。

  • 采用链式写法,可以指定一组按照顺序调用的回调函数。
const promise = new Promise((resolve,reject) => {
  // ...
}).then(r => {
  // ...
}).then(end=>{
  // ...
});

Promise.prototype.catch()

实例方法catch()是在.then(null,rejection)或者.then(undefined,rejection)发生错误的时候回调函数。

  • 如果异步操作抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数,处理这个错误。
  • 方法then指定的回调函数如果运行中抛出错误,也会被catch方法捕获.
  • 当Promise 状态已经变成resolved再抛出错误是无效的
  • Promise对象的错误具有"冒泡"性质,会一直向后传递,知道捕获为止。错误总会被下一个catch语句捕获。
  • 不在then方法里面定义reject状态的回调函数(then的第二个参数),总是使用catch方法
// 不好的写法
promise.then(function(data){
  // success
},function(error){
  // error
})// nice 的写法
promise.then(function(data){
  // success
}).catch(function(error){
  // error
});
catchtry...catch块的区别

如果没有使用cathch方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,使整个应用不会报错。

建议

Promise对象后面要跟着catch方法,这样可以处理Promise 内部发生的错误。catch方法返回的还是一个Promise对象,因此后面还是可以接着调用then方法的。

Promise.prototype.finally()

在***ES2018***,finally()方法用于指定不管Promise对象最后的状态如何,都会执行的操作。

const promise = new Promsie((resolve,reject) => {
  // ...
}).then(() => {
  // ...
}).catch(() => {
  // ...
}).finally(() => {
  // ...
})
  • finally方法里的操作,是与状态无关的,不依赖于Promise的执行结果。
  • finally本质上是then方法的特例
const promise = new Promise((resolve,reject)=>{
  // ...
});

promise.finally(()=>{
  // ...
});

promise.then(result=>{
  // ...
  return result;
},error=>{
  // ...
  throw error;
})
finally方法的实现
Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value => P.resolve(callback()).then(() => value);
    reason => P.resolve(callback()).then(() => {throw reason});
  );
};

Promise.all()

方法Promise.all()用于将多个Promise实例包装成一个新的Promise实例

方法Promise.all()参数必须具有Iterator接口,并且返回的每一个成员都是Promise实例

// p1,p2,p3 都是Promise的实例
const p = Promise.all([p1, p2, p3]);

上面的代码中,p的状态由p1,p2,p3决定。

p1,p2,p3的状态都是fulfilledp的状态才会成为fulfilled,此时,p1,p2,p3的返回值组成一个数组,传递给p的回调函数。

p1,p2,p3的状态有一个是rejectedp的状态就是rejected,此时,第一个被rejected的实例返回值,会传递给p的回调函数。

触发Promise.all()catch方法的条件
  • 如果作为参数的Promise实例已经定义了catch方法,那个它被rejected以后,并不会触发Promise.all()catch方法

  • 如果作为参数的Promise实例没有定义catch方法,那个它被rejected以后,并会触发Promise.all()catch方法,

Promise.race()

方法Promise.race()将多个Promise实例,包装成一个新的Promise实例。

// p1, p2, p3 都是 Promise 的实例
const promise = Promise.race([p1, p2, p3]);

在包装的Promise实例中,有一个实例率先改变状态,那么Promise.race()(上述代码中的promise)产生的实例的状态也跟着改变。

Promise.allSettled()

方法Promise.allSettled()接受一组Promise实例作为参数,包装成一个新的Promise实例,只有等到所有的参数实例都返回结果,不管结果是fulfilled还是rejected包装实例才会结束.

Promise.any()

方法Promise.any()接受一组Promise实例作为参数,包装成一个新的Promise实例,只要有一个参数实例状态是fulfilled包装实例就会变成fulfilled状态.如果所有参数实例都是reject状态,包装实例才会变成rejected状态。

Promise.resolve()

方法Promise.resolve()可以将现有对象转换为Promise对象

const jsPromise = Promise.resolve($.ajax('/whatever.json'));

上面的代码将jQuery生成的deferred对象,转为一个新的Promise对象

方法Promise.resolve()等价写法

Promise.resolve('foo'); 
// 等价于
new Promise(resolve => resolve('foo'));
参数
  • Promise实例
  • thenable对象

具体的thenable对象指具有then方法的对象,Promise.resolve方法将这个对象转为Promise对象,然后立即执行thenable对象的then方法

  • 参数不具有then方法,或者根本不是对象

方法Promise.resolve方法返回一个新的Promise对象,状态为resolved.

  • 不带任何参数

方法Promise.resolve()方法允许调用是不带参数,直接返回一个resolved状态的Promise对象

立即resolve()的Promise对象,是在本轮“事件循环(event loop)”的结束执行,而不是在下一轮“事件循环”的开始时。

Promise.reject()

方法Promise.reject()返回一个新的Promise实例,这个实例的状态为rejected.

方法Promise.reject()的参数,会原封不动的作为rejectreason,作为后续方法的参数。

应用

  • 图片加载

将图片加载写为一个Promise,一旦加载完成,Promise的状态就会发生改变

const preloadImage = function(path){
  return new Promise(function(resolve,reject){
    const image = new Image();
    image.onload = resolve;
    image.onerror = reject;
    image.src = path;
  });
};
  • Generator 函数与Promise 的结合

Promise.try()

使同步函数同步执行,异步函数异步执行,不做特殊区分,使它们具有统一的API的方法

  • 使用async函数
// f() 是同步的
const f = () => console.log('abc');
(async () => f())();
console.log('next');
// 'abc'
// 'next'

// f() 是异步的
(async () => f())()
.then(...)
.catch(...) 
  • 使用 new Promise()
const f = () => console.log('abc');
(
  ()=> new Promise(
  resolve => resolve(f())
  )
)();
console.log('next');

此时的同步函数f()也是可以立即执行的。

而方法Promise.try()可以代替上面的方法

const f = () => console.log('abc');
Promise.try(f());
console.log('next');

方法Promise.try()就是模拟try代码块,就像Promise.catch模拟catch代码块

备注:本文是自己学习阮一峰老师的《ECMAScript 6 入门》所做的笔记,大部分例子来源于此书。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值