Promise复习

316 阅读5分钟

复习Promise知识,关于它的概念,使用方式等,还有注意事项。 参考依然是阮一峰老师的 ESCMAScript6入门 es6.ruanyifeng.com/ ,写它的原因是在项目中能经常的使用到Promise做异步处理,,但对它并没有系统性的了解。

Promise对象

概念

简单说下,Promise是一种js单线程的异步处理方案,在对后端的接口请求中我们经常会用到,相信大多数前端都对这个不陌生。ES6将Promise进行来便准规范。

状态

promise有三种状态,并且具备了状态不受干扰以及状态一旦改变就不会再变的特性。

三种状态分别是

  1. pending进行中
  2. fulfilled已成功
  3. rejected已失败 需要注意的是,一旦执行,中途就无法取消,如果不设置回调函数,Promise内部会抛出错误,而且在pending状态中,不知道会进展到哪个阶段。

使用方式

在前端的使用过程中,一般在请求的时候都会用到,有的用来封装请求,也有在store中使用,当然也是封装请求的。大致如下

function getUserInfo(data) {
  return new Promise((resolve, reject) => {
    // 接口的方法
    if(true) {
      resolve('返回参数')
    } else {
      reject('错误的信息')
    }
  })
}
getUserInfo(data).then(res => {
  console.log(res)
}).catch(res => {
  console.log(res)
})

这个应该是最常见的使用方式,还有就是Promise.all()的方式,会在后边写到。

下边一个异步加载图片的例子

function loadImageAsync(url) {
  return new Promise((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
  })
}

这里比较重要的一点还是promise的已成功 和已失败两个回调,在得到图片路径的时候,使用resolve来返回正确的地址,错误的时候返回错误的地址。

执行过程

在promise中,一般是从进行中到已成功或已失败。在这个过程中是有回调地址的,如果当前回调地址为一个新的promise,那么当前这个promise的状态其实是取决于当前回调这个promise的状态的。

例如:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('p1'), 3000)
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve(p1), 1000)
})
p2.then(result => console.log(result,'result')) // p1 result
  .catch(error => console.log(error,'error'))

当前这个例子似乎是看不出来的,p2是成功回调,p1也是成功回调。对比第二个例子就能清晰看出来了

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error('错了')), 3000)
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve(p1), 1000)
})
p2.then(result => console.log(result,'result'))
  .catch(error => console.log(error,'error')) // Error: 错了

两个比较,我们就可以清晰认识到这一点。

then()和catch()

Promise 实例具有then方法,then方法是定义在原型对象Promise.prototype上的。then方法可以链式调用,通俗说就是葫芦娃救爷爷,一个接一个。

同样,promise.prototype上还有catch,上一个是处理回调的,这个是用来处理错误的。

两种等价的写法

// 写法一
const promise = new Promise(function(resolve, reject) {
  try {
    throw new Error('test');
  } catch(e) {
    reject(e);
  }
});
promise.catch(function(error) {
  console.log(error);
});

// 写法二
const promise = new Promise(function(resolve, reject) {
  reject(new Error('test'));
});
promise.catch(function(error) {
  console.log(error);
});
// 比较上面两种写法,可以发现reject()方法的作用,等同于抛出错误。

而且Promise 对象的错误会一直向后传递,直到被捕获

finally

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。相对用的比较少,在特定环境下,finally 也会有奇效,具体看业务场景。

all()

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

这个方法,在nuxtjs做服务端渲染的时候会用的比较多一些。用的方式也很特别。打包一起搞,全部返回后才走then方法。 状态有两种:全部为fulfilled,状态就是fuifilled;如果有一个是rejected,那么状态就为rejected.

全部为fulfilled

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

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

Promise.all([p1, p2])
.then(result => console.log(result,'then')) // ["p1", "p2"] "then"
.catch(e => console.log(e,'catch'));

有一个是rejected

const p1 = new Promise((resolve, reject) => {
  resolve('p1');
})
.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,'then'))
.catch(e => console.log(e,'catch'));

image.png

再看下面的例子

const p1 = new Promise((resolve, reject) => {
  resolve('p1');
})
.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,'then')) 
.catch(e => console.log(e,'catch'));

image.png

这个似乎我们之前所说的不一致,主要是因为p2中使用了catch, p2首先进入rejected,但是因为有自己的catch方法,方法会返回一个新的实例,p2指向的实际是这个实例。实例执行完成后也变为了resolved,因此p1,p2都进入了then方法。

race()

这个方法和all()相反,那个率先改变的 Promise 实例的返回值,就传递给回调函数

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

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

Promise.race([p1, p2])
.then(result => console.log(result,'then')) // p2 then
.catch(e => console.log(e,'catch'));

如果将定时器去掉,你会发现一直执行的都是p1

allSettled()

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

const promises = [
  fetch('/api-1'),
  fetch('/api-2'),
  fetch('/api-3'),
];

await Promise.allSettled(promises);
removeLoadingIndicator();

any()

该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。

Promise.any([
  fetch('https://v8.dev/').then(() => 'home'),
  fetch('https://v8.dev/blog').then(() => 'blog'),
  fetch('https://v8.dev/docs').then(() => 'docs')
]).then((first) => {  // 只要有一个 fetch() 请求成功
  console.log(first);
}).catch((error) => { // 所有三个 fetch() 全部请求失败
  console.log(error);
});

以上就是回顾的所有知识点,有不足指出,还请多多指教!!!

相关文章

let,const复习 juejin.cn/post/699147…

初学koa搭建项目 juejin.cn/post/698756…

正则表达式总结 juejin.cn/post/698615…

flex布局总结 juejin.cn/post/698497…

mongodb基础用法 juejin.cn/post/698364…

vue3搭建管理后台-项目搭建 juejin.cn/post/696802…

工厂模式,构造器模式和原型模式 juejin.cn/post/695794…