Promise

目录

一、特点

二、基本用法

1、创建

2、then指定状态的回调函数

3、执行顺序

4、实例方法then

5、实例方法catch

跟传统的try/catch代码块不同的是:

6、实例方法finally

手写finally()

7、实例方法all()

8、实例方法race

9、实例方法allSettled()

10、实例方法any

11、实例方法resolve()

12、实例方法reject() 

13、实例方法try

三、应用

1、promise包装图片异步加载

2、Generator 函数与 Promise 的结合


异步编程的一种解决方案。

一、特点

1、对象的状态不受外界影响。

Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

2、一旦状态改变,就不会再变,任何时候都可以得到这个结果。

Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。

3、缺点:

  • 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  • 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  • 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

二、基本用法

1、创建

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

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

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

  • resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
  • reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

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

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

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

p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作
这时p1的状态就会传递给p2,p1的状态决定了p2的状态。
如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变
如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行

2、then指定状态的回调函数

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

then方法可以接受两个回调函数作为参数。

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

3、执行顺序

Promise 新建后就会立即执行

then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。

4、实例方法then

定义在原型对象Promise.prototype上,作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。

返回值:一个新的Promise实例

链式写法:then方法后面再调用另一个then方法,前一个then的回调函数完成以后的结果传给第二个then的回调函数

5、实例方法catch

用于指定发生错误时的回调函数,是.then(null, rejection).then(undefined, rejection)的别名

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

getJSON()方法返回一个 Promise 对象
如果该对象状态变为resolved,则会调用then()方法指定的回调函数;
如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误
另外,then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获

Promise 在resolve语句后面,再抛出错误,不会被捕获,等于没有抛出。因为 Promise 的状态一旦改变,就永久保持该状态,不会再变了。Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获

建议!不要在then()方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法

跟传统的try/catch代码块不同的是:

如果没有使用catch()方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应

6、实例方法finally

作用:指定不管 Promise 对象最后状态如何,都会执行的操作。

参数:回调函数,但回调函数不接受任何参数。=》说明:finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。

本质:finally本质上是then方法的特例

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

// 等同于
promise
.then(
  result => {
    // 语句
    return result;
  },
  error => {
    // 语句
    throw error;
  }
);

如果不使用finally方法,同样的语句需要为成功和失败两种情况各写一次。
有了finally方法,则只需要写一次。

不管promise最后的状态,在执行完thencatch指定的回调函数以后,都会执行finally方法指定的回调函数

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

finally方法总是会返回原来的值

// resolve 的值是 undefined
Promise.resolve(2).then(() => {}, () => {})

// resolve 的值是 2
Promise.resolve(2).finally(() => {})

// reject 的值是 undefined
Promise.reject(3).then(() => {}, () => {})

// reject 的值是 3
Promise.reject(3).finally(() => {})

手写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 })
  );
};

7、实例方法all()

作用:将多个 Promise 实例,包装成一个新的 Promise 实例

参数:一个数组(或者Promise.resolve方法且将参数转为 Promise 实例的对象),数组里的元素都是promise实例。如果数组中存在不是promise实例的元素,先调用Promise.resolve方法,将参数转为 Promise 实例

Promise.all()的状态的决定:.

  • 数组里所有的promise实例的状态都是fullfilled,Promise.all()的状态就是fullfilled,此时数组里元素的返回值组成一个数组,传递给Promise.all()的回调函数
  • 数组里的promise实例只要有一个的状态是rejected,Promise.all()的状态就会变成rejected,数组里第一个被rejected的promise实例的返回值传给Promise.all()的回调函数

如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()catch方法

const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result)
.catch(e => e);

const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了');
})
.then(result => result)
.catch(e => e);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 报错了]

p1会resolved,p2首先会rejected,但是p2有自己的catch方法,该方法返回的是一个新的 Promise 实例,p2指向的实际上是这个实例。
该实例执行完catch方法后,也会变成resolved,导致Promise.all()方法参数里面的两个实例都会resolved,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数。

--------------------------------------------------------------

当p2没有自己的catch方法,就会调用Promise.all()的catch方法
const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result);

const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了');
})
.then(result => result);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// Error: 报错了

8、实例方法race

作用:将多个 Promise 实例,包装成一个新的 Promise 实例

参数:一个数组,数组里的元素都是promise实例。如果不是 Promise 实例,就会先调用Promise.resolve()方法,将参数转为 Promise 实例

Promise.race()状态的决定:数组里的promise实例只要有一个实例率先改变状态,这个实例的返回值就传给Promise.race(),Promise.race()的状态就跟着改变,

9、实例方法allSettled()

作用:确定一组异步操作是否都结束了(不管成功或失败)。希望等到一组异步操作都结束了,不管每一个操作是成功还是失败,再进行下一步操作。

参数:一个数组,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。

返回值:一个新的promise实例。状态发生改变后状态总是fulfilled

const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);

const allSettledPromise = Promise.allSettled([resolved, rejected]);

allSettledPromise.then(function (results) {
  console.log(results);
});
// [
//    { status: 'fulfilled', value: 42 },
//    { status: 'rejected', reason: -1 }
// ]

Promise.allSettled()的返回值allSettledPromise,状态只可能变成fulfilled。(对应上面返回值的知识点)
它的回调函数接收到的参数是数组results。该数组的每个成员都是一个对象,对应传入Promise.allSettled()的数组里面的两个 Promise 对象。

results的每个成员是一个对象,对象的格式是固定的,对应异步操作的结果

参数数组中的成员对象的status属性的值只可能是字符串fulfilled或字符串rejected,用来区分异步操作是成功还是失败。如果是成功(fulfilled),对象会有value属性,如果是失败(rejected),会有reason属性,对应两种状态时前面异步操作的返回值

const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);

// 过滤出成功的请求
const successfulPromises = results.filter(p => p.status === 'fulfilled');

// 过滤出失败的请求,并输出原因
const errors = results
  .filter(p => p.status === 'rejected')
  .map(p => p.reason);

10、实例方法any

作用:只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

返回值:包装成一个新的 Promise 实例返回

参数:一组 Promise 实例

与Promise.race()的区别:Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束

11、实例方法resolve()

作用:将现有对象转为 Promise 对象

参数:

  • 参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例
  • 参数是一个thenable对象(具有then方法的对象)Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法
  • 如果参数是一个原始值,或者是一个不具有then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,状态为resolved
  • Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象

12、实例方法reject() 

返回值:一个新的promise实例,该实例的状态为rejected

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数

Promise.reject('出错了')
.catch(e => {
  console.log(e === '出错了')
})
// true

13、实例方法try

用法:不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误

三、应用

1、promise包装图片异步加载

function loadImageAsync(url) {
  return new Promise(function(resolve, reject) {
    const image = new Image();

    image.onload = function() {
      resolve(image);
    };

    image.onerror = function() {
      reject(new Error('Could not load image at ' + url));
    };

    image.src = url;
  });
}

2、Generator 函数与 Promise 的结合

使用 Generator 函数管理流程,遇到异步操作的时候,通常返回一个Promise对象

function getFoo () {
  return new Promise(function (resolve, reject){
    resolve('foo');
  });
}

const g = function* () {
  try {
    const foo = yield getFoo();
    console.log(foo);
  } catch (e) {
    console.log(e);
  }
};

function run (generator) {
  const it = generator();

  function go(result) {
    if (result.done) return result.value;

    return result.value.then(function (value) {
      return go(it.next(value));
    }, function (error) {
      return go(it.throw(error));
    });
  }

  go(it.next());
}

run(g);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值