目录
1.前言:
2.本篇文章的目的有三个:
3.作用:
4.格式:
5. 链式使用
6.链式调用的核心
7. 项目中的回调地狱和代码优化
8.Promise常见面试题整理
8.1 Promise 有几种状态,什么时候会进入 catch?
8.2 setTimeout、Promise、Async/Await 的区别?
8.3 、Promise 构造函数是同步执行还是异步执行,那么 then 方 法呢?
8.4 Promise 只有成功和失败 2 个状态,怎么让一个函数无论成 功还是失败都能被调用?
8.5 Promise 中 reject 和 catch 处理上有什么区别
8.6 手写一个 Promise(高薪常问)
8.7 Promise 如何封装一个 Ajax(高薪常问)
8.8 分析下列程序代码,得出运行结果,解释其原因
1.前言:
本文前半部分讲述如何使用,后面讲述promise的核心功能,最后是整理的面试题。
注:文中所有代码都可直接复制在浏览器控制台运行。
2.本篇文章的目的有三个:
第一,会用Promise
第二,能看懂别人写的Promise代码
第三,能够回答面试中大多数的promise相关问题
3.作用:
解决回调地狱问题,另一种异步代码的写法(之前就是回调函数)。
注:若有人问,一定能解决回调地狱问题吗?答案是不一定——必须是链式写法且不能嵌套才可以
4.格式:
4.1 promise的格式:
有三个状态,和一个值。注意初始状态pending啥也不是,可理解为一个起点,要么指向成功,要么指向失败。
<script>
var p1 = new Promise((resolve, reject)=>{
// 异步代码,这个区间的代码是同步的,在then里面的才是异步的,后面会介绍,别误会了。
resolve({a:1})
//reject({a:1})
})
// promise对象:
// 1. 状态 pending, resolved(fulfilled) 成功, rejected 失败
// 2. 值。resolve,reject调用时传入的数据
console.log(p1)
</script>
4.2 控制台打印如下:
4.3 小结:
promise对象有三个状态:pending(初始), resolved成功(实测浏览器显示fulfilled,resolved是之前大家伙默认的),rejected 失败
resolve, reject是两个形参,且是函数
5. 链式使用
最核心也是最常见的方法,务必掌握。
当p1的状态从pending --> resolved时会执行then函数
当p1的状态从pending --> rejected时会执行catch函数
then、catch函数里的参数(此处是obj)代表promise里面的值
<script>
var p1 = new Promise((resolve, reject)=>{
// 异步代码
// resolve({a:1})
setTimeout(() => {
// resolve({a:1})
reject('abc')
},3000)
})
// 当p1的状态从pending --> resolved时会执行
p1.then((obj)=>{
// obj就是p1的promiseValue
console.log('then...', obj)
}).catch(err => { // 当p1的状态从pending --> rejected时会执行
// err就是p1的promiseValue
console.log('catch...',err)
})
</script>
控制台打印:
6.链式调用的核心
链式调用时,会返回一个新的promise对象
链式调用时,如果返回值不是promise,则状态一定为成功,值为链式调用的参数。
链式调用时,如果返回值是promise,则状态是promise的状态,值为promise对象的值。
返回值没有,如不写return时,返回值是undefined
<script>
var p1 = new Promise((resolve, reject)=>{
resolve({a:1})
})
var p2 = p1.then(obj => {
// return 100 返回值不是promise,所以状态是成功,返回值是100
let p = new Promise((res,rej)=>{
rej(123)
})
return p
})
// 它返回值是一个新的promise对象
// p2的proimseState,promiseValue如何决定?
// (1) 如果返回值val不是Promise, 则p2的proimseState====成功 , promiseValue===val
// (2) 如果返回值val是Promise对象,
// 则p2的proimseState====Promise对象的状态 , promiseValue===Promise对象的val
// console.log(p2)
const p3 = p2.then(res=>{
console.log(res) //注意:这里没有return 默认return是underfined
}).catch(err=>{
console.log('err',err)// 123
})
console.log('p3',p3)
</script>
6.1重要结论:
如果没有返回promise对象的话,可以无限的点then,这个then继承上一个then的状态和值,可以一直链式下去。直到遇到return 一个promise对象才会根据这个对象的状态和值,不再继承之前的
7. 项目中的回调地狱和代码优化
7.1 回调地狱的写法
7.2 改成链式调用(代码优化)
7.3 项目实际代码:
// 修改sku
updateCartSku (ctx, { newSku, oldSkuId }) {
console.log('updateCartSku', newSku)
return new Promise((resolve, reject) => {
if (ctx.rootState.user.profile.token) {
console.log('按登录的去做')
const oldSku = ctx.state.list.find(it => it.skuId === oldSkuId)
const count = oldSku.count // 获取数据
// 1. 删除
// 2. 添加
// 3. 获取最新的购物车
deleteCart([oldSkuId])
.then(() => insertCart({ skuId: newSku.skuId, count }))
.then(() => findCartList())
.then(data => {
ctx.commit('setCartList', data.result)
resolve()
})
} else { // 没有登录
ctx.commit('updateCartSku', { newSku, oldSkuId })
resolve()
}
})
},
7.4 小结:
用Promise确实可以解决回调地狱的问题,但要注意,要链式写,而不要嵌套,不然亦然陷入回调地狱的问题
8.Promise常见面试题整理
8.1 Promise 有几种状态,什么时候会进入 catch?
Promise 有几种状态
三个状态:pending、fulfilled、reject
两个过程:padding -> fulfilled、padding -> rejected Promise
什么时候会进入 catch 当 pending 为 rejectd 时,会进入 catch
8.2 setTimeout、Promise、Async/Await 的区别?
事件循环中分为宏任务队列和微任务队列
宏任务(macrotask):在新标准中叫 task 主要包括:script(整体代码),setTimeout,setInterval,setImmediate,I/O,ui
微任务(microtask):在新标准中叫 jobs 主要包括:process.nextTick,Promise,MutationObserver(html5 新特性)
setTimeout、Promise、Async/Await 的区别
setTimeout 的回调函数放到宏任务队列里,等到执行栈清空以后执行
Promise.then 里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执 行完再执行
async 函数表示函数里面可能会有异步方法,await 后面跟一个表达式
async 方法执行时,遇到 await 会立即执行表达式,然后把表达式后面的代码放到微任务队 列里,让出执行栈让同步代码先执行
8.3 、Promise 构造函数是同步执行还是异步执行,那么 then 方 法呢?
Promise 构造函数是同步执行的,then 方法是异步执行
8.4 Promise 只有成功和失败 2 个状态,怎么让一个函数无论成 功还是失败都能被调用?
使用 Promise.all()
Promise.all()用于将多个 Promise 实例,包装成一个新的 Promise 实例
Promise.all()接受一个数组作为参数,数组里的元素都是 Promise 对象的实例,如果不是, 就会先调用下面讲到的 Promise.resolve(),将参数转为 Promise 实例,再进一步处理。
(Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都 是 Promise 实例。
8.5 Promise 中 reject 和 catch 处理上有什么区别
reject 是用来抛出异常,catch 是用来处理异常
reject 是 Promise 的方法,而 catch 是 Promise 实例的方法
reject 后的东西,一定会进入 then 中的第二个回调,如果 then 中没有写第二个回调,则进入 catch
网络异常(比如断网),会直接进入 catch 而不会进入 then的第二个回调
8.6 手写一个 Promise(高薪常问)
var Promise = new Promise((resolve, reject) => {
if (操作成功) {
resolve(value)
} else {
reject(error)
}
})
Promise.then(function (value) {
// success
}, function (value) {
// failure
})
8.7 Promise 如何封装一个 Ajax(高薪常问)
8.8 分析下列程序代码,得出运行结果,解释其原因
<script>
var p1 = new Promise((resolve, reject) => {
resolve('success1')
reject('error')
resolve('success2')
})
p1.then((res) => {
console.log('then: ', res)
}).catch((err) => {
console.log('catch: ', err)
})
console.log(p1); // then: success1
</script>
解析:构造函数中的 resolve 或 reject 只有第一次执行有效,多次调用没有任何作用,呼 应代码二结论:Promise 状态一旦改变则不能再变