ES6学习笔记(十四)Promise对象

promise是用于实现异步编程的一种解决方案,异步操作保存在promise中,在promise状态发生改变时触发对应的异步操作。

Promise的特点


  1. promise有三种状态,pending(进行中),resolve(已成功),rejected(已失败)。只有异步操作的结果才能改变promise的状态,其他操作无法改变其状态。
  2. Promise的状态转换只有两种,从pending转为resolve或者从pending转为rejected。一旦转换完成,状态就不再改变。如果状态改变已经发生了,再调用这个Promise对象,会立即返回结果。这与事件不同,事件一旦错过发生的时候,再监听该事件不会得到结果。
  3. Promise无法取消,一旦新建就会立即执行
  4. 其内部抛出的错误不会反应到外部
  5. 处于pending状态时,无法得知当前所处的状态(刚开始进行还是即将完成)

Promise的基本用法


Promise使用Promise的构造方法来构建,该构造方法传入一个函数作为参数,传入的函数有两个参数,resolve和reject。resolve用于将状态由pending转为resolve,reject用于将pending转为rejected。

const promise = new Promise(function(resolve, reject) {

  // ... some code

  if (/* 异步操作成功 */){

    resolve(value);

  } else {

    reject(error);

  }

});

resolve和reject中的参数将作为实例生成后then方法中的参数函数中的对应参数。

promise.then(function(value) {

  // success

}, function(error) {

  // failure

});

then方法第一个参数为异步操作成功的回调函数,即resolve,第二个参数为异步操作失败的回调函数,即rejected。

then方法的回调函数在当前脚本所有同步任务执行完后才会执行,所以下面的代码的执行顺序与代码顺序不同。

var pro = new Promise((resolve, reject) => {

resolve();

console.log('promise');

});

pro.then(function() {

console.log('resolved');

});



console.log('out');

//promise

//out

//resolved

上面的代码可以看到,promise新建时立即执行了里面的console语句,接着将当前脚本中的同步任务即全局下的console.log(‘out’)执行后才执行then里面的console语句。

resolve和reject函数的参数也可以是另一个promise,此时当前promise的状态由参数的promise决定。

var p1 = new Promise(

    (resolve, reject) => {

        resolve(p2);

    });



var p2 = new Promise(

    (resolve, reject) => {

        resolve('p2');

    }

)



p1.then((value) => {

    console.log(value)

})

// p2

调用resolve和reject后接下来的语句仍会执行

var p1 = new Promise(

  (resolve, reject) => {

    resolve('resolve');

    console.log('after');

});

p1.then((value) => {

  console.log(value)

})

// after

// resolve

为了避免这种情况,我们将resoleve放在return后

var p1 = new Promise(

  (resolve, reject) => {

    return resolve('resolve');

    console.log('after');

});



p1.then((value) => {

  console.log(value)

})

// resolve

Promise的方法


Promise的原型方法


Promise.prototyep.then

then方法用于为Promise提供状态改变时添加对应的回调函数,其两个参数都为函数,第一个参数为Promise状态转为resolve时的回调函数,第二个参数为Promise状态转为rejected时的回调函数。该方法返回一个新的Promise实例(不是原来的Promise对象),即可以使用链式写法多次调用then方法。

getJSON("/posts.json")

.then(function(json) {

  return json.post;

}).then(function(post) {

  // ...

});

多个then方法链式调用时,会等待上一个then方法返回后才执行下一个。

Promise.prototype.catch

Promise.prototype.catch(reject)可以看成是Promise.prototype.then(null,reject)或Promise.prototype.then(undefined,reject),即该方法是Promise转换状态时发生错误的回调函数。

const promise = new Promise(function(resolve, reject) {

  throw new Error('test');});

promise.catch(function(error) {

  console.log(error);});

// Error: test

上面代码可以看到,在Promise中抛出了一个错误,catch方法捕获了这个错误。

若Promise已经进入了resolve状态,则抛出错误不会被catch方法捕获,这是因为Promise状态只要转换成resolve或rejected就不会再变换状态的特点导致的。

const promise = new Promise(function(resolve, reject) {

  resolve('ok');

  throw new Error('test');});

promise

  .then(function(value) { console.log(value) })

  .catch(function(error) { console.log(error) });

// ok

上面代码中,promise已经调用了resolve,所以其下面的抛出异常语句没有被catch方法捕获。

Promise对象抛出的错误如果没被捕获会传递到返回的Promise实例上,知道被捕获为止。

getJSON('/post/1.json').then(function(post) {

  return getJSON(post.commentURL);})

.then(function(comments) {

  // some code

}).catch(function(error) {

  // 处理前面三个Promise产生的错误

});

上面代码中有三个Promise对象,不管哪一个抛出异常,最后都由catch来捕获。

如果没有指定处理错误的回调函数,Promise抛出的错误不会有任何反应,不会影响到外层代码的执行。

const someAsyncThing = function() {

  return new Promise(function(resolve, reject) {

// 下面一行会报错,因为x没有声明    

resolve(x + 2);

  });

};

someAsyncThing().then(function() {

  console.log('everything is great');

});

setTimeout(() => { 

console.log(123) 

}, 2000);

// 123

如上面代码中虽然Promise报错了,但是外面的setTimeout方法依然可以执行。

不仅是Promise中会抛出错误,回调函数中也会抛出错误,报错catch中的函数,此时可以再用一个catch来处理抛出的错误。

someAsyncThing().then(function() {

  return someOtherAsyncThing();

}).catch(function(error) {

  console.log('oh no', error);

  // 下面一行会报错,因为y没有声明  

y + 2;})

.catch(function(error) {

  console.log('carry on', error);

});

// oh no [ReferenceError: x is not defined]

// carry on [ReferenceError: y is not defined]

Promise.prototype.finally

该方法用于执行Promise不管转换成什么状态都会执行的操作,可以类比try/catch操作中的finally。

Promise

.then(result => {···})

.catch(error => {···})

.finally(() => {···});

如上代码中,不管promise最后变为什么状态,finally后的参数函数一定会执行。

finally方法的回调函数不接受参数,这意味着无法得知当前是什么状态,所以finally用来执行与状态无关的操作,其本质为then方法的特例。其实现如下代码。

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 })

  );

};

finally方法最后会返回resolve或reject的参数值。

// resolve 的值是 undefined

Promise.resolve(2).then(() => {}, () => {})

// resolve 的值是 2

Promise.resolve(2).finally(() => {})

// reject 的值是 undefined

Promise.reject(3).then(() => {}, () => {})

// reject 的值是 3

Promise.reject(3).finally(() => {})

转为Promise对象的方法


Promise.resolve

该方法用于将参数转为Promise对象

Promise.resolve('foo')

// 等价于

new Promise(resolve => resolve('foo'))

由上面的等价写法我们可以看出Promise.resolve方法返回一个转换为resolve状态的Promise对象,根据其参数有四种不同的返回情况

1.参数为一个Promise实例

此时该方法会直接返回参数中的Promise实例

2.参数是一个有then方法的对象

var thenObj = {

  then(resolve) {

    console.log('then');

    resolve('resolve');

  }

}



Promise.resolve(thenObj).then(value => {

  console.log(value);

})

// then

// resolve

在使用该方法将对象转为Promise对象时会立即执行该对象的then方法,所以上面代码中首先打印出’then’,然后才执行resolve的回调函数。

3.参数为一个没有then方法的对象或非对象

此时该方法返回resolve的参数为该方法参数的Promise对象

const p = Promise.resolve('Hello');



p.then(function (s){

  console.log(s)

});

// Hello

4.没有参数

此时该方法直接返回一个Promise对象,由于没有参数,所以此时打印出resolve的参数只能得到undefined

var p = Promise.resolve();

p.then(function (value) {

  console.log(value);

});

// undefined

Promise.reject

该方法也接受一个参数,返回一个Promise对象,与Promise.resolve不同的是,该方法返回的Promise对象的状态为rejected。

const p = Promise.reject('出错了');

// 等同于

const p = new Promise((resolve, reject) => reject('出错了'))



p.then(null, function (s) {

  console.log(s)});

// 出错了

对于不同参数的处理,Promise.reject与Promise.resolve一样

Promise处理多个Promise的方法


Promise.all

该方法用于将多个Promise实例包装为一个新的Promise实例。

该方法接受一个数组作为参数,数组成员为Promise对象,若不是Promise对象,则先使用Promise.resolve方法变为对象。

该方法返回的Promise对象的状态由参数数组中的Promise对象决定,若数组中所有Promise对象的状态都变成已成功,则该方法返回的Promise对象的状态也变为已成功,数组中所有Promise的返回值组成一个数组传递给该方法生成的Promise对象的回调函数;若数组中有一个Promise对象状态变为已失败,则第一个变为已失败的对象的返回值传递给最后返回的Promise对象,该对象状态也变为已失败。

// 生成一个Promise对象的数组

const promises = [2, 3, 5, 7, 11, 13].map(function (id) {

  return getJSON('/post/' + id + ".json");

});

Promise.all(promises).then(function (posts) {

  // ...

}).catch(function(reason){

  // ...

});

上面代码中Promise.all的参数数组中有六个成员,只有六个都变为已成功或其中有一个变为已失败,promises的状态才会发生改变,才回调用其回调函数。

Promise.race

该方法的参数和Promise.resolve一样,不同的是,其状态由参数数组成员中率先改变的Promise对象决定,返回的Promise对象会与率先改变的Promise对象相同,率先改变的Promise对象的返回值也会传递给该方法返回的Promise对象。

如果参数数组中的成员不是Promise对象,一样会先使用Promise.resolve将其变为对象。

 

其他方法


Promise.try

该方法用于模拟try代码块,通过该方法可以使同步函数同步执行,异步函数异步执行。

如下面代码中,database.users.get可能会抛出同步错误也可能抛出异步错误,对应不同的错误写法如下。

//异步错误

database.users.get({id: userId})

.then(...)

.catch(...)

//同步错误

try {

  database.users.get({id: userId})

  .then(...)

  .catch(...)} catch (e) {

  // ...}

而通过使用Promise.try可以同时处理这两种错误

Promise.try(() => database.users.get({id: userId}))

  .then(...)

  .catch(...)

 


参考自阮一峰的《ECMAScript6入门》

 

猜你喜欢

转载自blog.csdn.net/zemprogram/article/details/86564055
今日推荐