前端Promise最全笔记
目录
- 前言
- 什么是Promise?
- Promise对象的特点
- Promise 与 resolve 的使用方法
- reject() 的用法
- catch的用法
- Promise.all的用法
- Promise.race的用法
- Promise.any()的用法
- 面试题:Promise 代码执行顺序
- 面试题:Promise的状态
- finally()方法(使用较少)
- 面试题:async 和 await 相关
- 面试题:Promise中抛出错误
- 面试题: 综合题目
前言
- JS是单线程的,想要提高JS的能力,异步相关的解决方法和原理都是非常值得学习的
- 这一份Promise笔记是我自己看了大量其他作者的笔记进行整理的,可能也有理解不透彻的地方,若有问题欢迎留言指正,谢谢!
- 本文旨在详细解读Promise的基本使用和方法,不涉及原理层面的探讨,所以如有啰嗦的地方还请见谅,希望可以帮助到小白成长~
- 如果你静下心来认真看一遍,相信会有一定的帮助
什么是Promise?
Promise是异步编程的一种解决方案,其实是一个构造函数,本身有 all、any、race、reject、resolve方法,原型上有 then、catch方法
Promise对象的特点
- 对象的状态不受外界影响
一共有3种状态 ① pending进行中;② fulfilled已成功;③rejected已失败 只有异步操作的结果可以决定当前是哪一种状态,其他的任何操作都不会改变promise的状态
- 状态一旦确定就不会再改变,且任何时候都可以得到这个状态结果
Promise对象的状态改变有两种可能:① 从pending转变为fulfilled;② 从pending转变为rejected。只要这两种情况发生了,那么promise就会一直保持这个状态的结果,称为状态定型
Promise 与 resolve 的使用方法
通过new关键字创建一个Promsie对象以后,promise对象会自动执行内部回调函数,看下列代码:
let p = new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('执行完成Promise');
resolve('返回resolved状态');
}, 2000);
// 2s后会执行输出语句输出 执行完成Promise
上述代码中,不仅执行回调函数中的输出语句,同时也通过resolve()方法返回了promise对象的状态(或者说将对象的状态修改为成功),意思就是当前的promise的对象已经从pending状态变为resolved状态,且状态不会再发生改变
针对上述创建Promise对象就会自动执行回调函数的情况,我们在实际使用过程当中一般是将构造Promise对象的过程封装在一个箭头函数中,然后return返回promise对象
// 使用匿名函数 promiseClick() 封装
const promiseClick = () => {
console.lo***击方法被调用')
let p = new Promise(function(resolve, reject) {
//做一些异步操作
setTimeout(function() {
console.log('执行完成Promise');
resolve('promise状态变为resolved');
}, 2000);
});
return p
}
调用promiseClick()函数的时候,首先执行的是console.log输出语句,然后构造一个promise对象,最后return返回该对象。因此,我们调用该函数的时候,就可以紧跟着使用promise身上的 .then()方法
promiseClick().then(function(data) {
console.log(data) //输出是:promise状态变为resolved
})
.then() 方法中可以通过逗号分隔,定义两个回调函数,第一个回调函数是处理resolved状态,第二个是处理rejected转态。对于上述代码来说,调用promiseClick()后返回的promise对象的状态是 resolved,因此.then()中的回调函数接收的data参数值就是resolve()中返回的状态值,即【promise状态变为resolved】
.the()方法的作用就是重新调用函数,所以在有多层回调的时候,就在上一级.then()方法中返回一个Promsie对象,然后就可以继续使用 .then()方法进行回调,从而解决了回调地狱的问题
reject() 的用法
上述提到的Promise与resolve的使用,简单来说就是Promise对象成功回调的时候,resolve将Promise对象的状态修改为fulfilled,那么Promise对象调用失败的时候呢?
reject()就是将Promise对象的状态修改为rejected(已失败),请注意它只是修改promise对象的状态
刚才提到的then()方法中可以定义两个回调函数,如果只定义了一个就是只处理resolved的情况,所以定义第二个的时候就可以用来处理rejected的情况
function promiseClick() {
let p = new Promise(function(resolve, reject) {
setTimeout(function() {
var num = Math.ceil(Math.random() * 20); //生成1-10的随机数
console.log('随机数生成的值:', num)
if (num <= 10) {
resolve(num);
} else {
reject('数字大于10了即将执行失败回调');
}
}, 2000);
})
return p
}
promiseClick().then(
function(data) {
console.log('resolved回调成功');
console.log('resolved状态值:', data);
},
function(reason) {
console.log('rejected回调失败');
console.log('rejected状态的原因:', reason);
}
);
catch() 的用法
catch()是与then()并行的一个方法,作用是捕获异常,具体来说是: ① 可以代替then()方法中的第二个回调函数,用来捕获rejcted状态;② 第二个作用比较重要,如果resolved状态下的回调函数在执行过程中抛出了异常,当紧跟then()方法使用catch()方法以后,catch中就可以解决这个异常,从而不会导致阻塞js的运行,异常的原因也会传到catch中
作用一:
promiseClick().then(
function(data) {
console.log('resolved回调成功');
console.log('resolved状态值:', data);
}
)
.catch(function(reason, data) {
console.log('rejected状态:', data);
});
作用二:
promiseClick().then(
function(data) {
console.log('resolved回调成功');
console.log('resolved状态值:', data);
}
)
.catch(function(reason, data) {
console.log('异常原因:', reason);
});
Promise.all()的用法
all()是与the()同级的一个方法,该方法的特点是:提供了并行执行异步操作的能力,并且在所有的异步操作执行完成且所有异步操作的状态是resolved状态才会执行回调函数
下列代码,数组中全部都是封装好的构造promise对象的函数名(上面提到过),执行这些函数以后,均会返回一个promise对象且带有resolved或rejected状态
Promise
.all([promiseClick3(), promiseClick2(), promiseClick1()])
.then(function(results) {
console.log(results);
});
只有当数组中的所有函数执行完,且全部返回带有resolve状态的promise对象后,才会紧跟着去执行then()方法中的回调函数
then()方法接收的results指的就是上述数组中返回的状态结果值,这个状态结果值也是以数组的形式传递到then()方法中,从而在then()的回调函数中就可以处理所有的返回的数据
举个例子:promiseClick3(), promiseClick2(), promiseClick1()返回的resolved状态值分别是:1,5, 3,那么results接收的值就是数组 [1, 5, 3]
Promise.race()的用法
race()与all()方法相似但又有不同之处,通过race的中文含义就可以感受到。race()方法是:数组中调用的函数,谁先返回Promise对象并带有状态值(可以是resolved也可以是rejectd),那么就先执行谁,其他的promise对象的异步操作就不会再进入到race()中,这一点需要注意一下,并不是按照先后顺序执行,只会执行第一个进来的promise对象处理并处理对应的状态值
Promise
.race([promiseClick3(), promiseClick2(), promiseClick1()])
.then(function(results) {
console.log('成功', results);
}, function(reason) {
console.log('失败', reason);
});
使用race()的时候,后面的then()方法中一般定义两个回调函数,因为最先进来的promise对象的状态值可能是resolved也可能是rejected。如果then()中只定义了一个回调函数,也可以通过在then()的后面定义一个catch()方法,catch也是可以接捕获rejected状态的
Promise.any()的用法
只要any()的数组中有一个Promise对象是resolved,就会执行回调输出结果
Promise.race() 返回的是第一个结果(有可能是resolve/reject),但是any返回的一定是第一个成功的结果(resolve),any会忽略掉出现resolve之前所有的reject
面试题:Promise 代码执行顺序
-
涉及代码执行顺序时候,就需要考虑是:同步任务还是异步任务,异步任务又需要细分为微任务与宏任务。我不深入讲区别,简单来说一下执行顺序的原则
-
代码从上往下执行的时候,遇到同步任务立即执行,遇到微任务就暂时放入微任务队列中暂不执行,遇到宏任务就暂时放入宏任务队列暂不执行。同步任务全部执行完毕,才从微任务队列中取出微任务依次执行。微任务全部执行完毕,才从宏任务队列中取出宏任务依次执行
-
同步任务代码中可能会包括同步任务、微任务、宏任务,微任务和宏任务也都有这种可能,但是无论如何,严格按照第2条说到的原则执行
任务类型 | 任务 |
---|---|
微任务 | Promise、async/await |
宏任务 | 定时器、ajax、DOM操作 |
new Promise((resolve, reject) => {
console.log('new Promise')
resolve('success')
})
console.log('finifsh')
// 输出结果顺序:
new promise
finifsh
-
resolve()只是确定了当前promise的转态,不会产生输出的操作
-
考察new创建Promise对象就会立即执行回调函数,new的过程是同步的
Promise.resolve(1)
.then(res => {
console.log(res) // => 1
return 2 // 包装成 Promise.resolve(2)
})
.then(res => {
console.log(res) // => 2
})
// 输出结果是:
1
2
-
Promise.resolve()表示promise对象的状态已经确定,是resolved状态,所以就可以执行then()中的回调函数
-
需要注意的是:Promise.resolve()是微任务,是次于同步任务执行的。但是这一段代码中没有同步任务,所以就直接执行微任务
const promise = new Promise((resolve, reject) => {
console.log(1);
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);
// 输出结果是
1
2
4
-
首先是new一个Promise对象,会立即执行回调函数,正好回调函数中都是同步任务,所以输出1 2
-
promise.then()是一个微任务,放入微任务队列暂时不管
-
执行同步任务输出4
-
同步任务执行完毕,取出微任务promise.the(),但是这个promise对象在创建的时候没有返回任何状态值,因此then()中的回调函数无法执行,整个代码执行完毕
const promise1 = new Promise((resolve, reject) => {
console.log('promise1')
resolve('resolve1')
})
const promise2 = promise1.then(res => {
console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);
// 输出结果是:
promise1
1 Promise { 'resolve1' }
2 Promise { <pending> }
resolve1
-
new一个promise1,回调函数立即执行,执行输出语句,执行resolve()使得promise1有了resolved状态值resolve1
-
promise1.then()是微任务,暂存微任务队列
-
执行两个同步任务输出语句,输出promise对象实则就是输出当前Promise对象的状态以及状态值,输出结果是以对象的形式呈现
-
同步任务执行完毕,取出微任务执行,promise1.then()中的res接收的是resolved值【resolve1】,所以输出resolve1
-
代码全部执行完毕
-
补充一点:promise2的作用是用来接收then()中返回一个新的Promise对象,但是实际上没有返回Promise对象给promise2