一、promise
作用:一种新的异步代码封装方式,用来代替回调函数的
promise 的三个状态:
-
1.持续:
pending -
2.成功:
fulfilled -
3.失败:
rejected
promise 只会在持续状态转换为成功或者从持续状态转换为失败
promise 的基本语法:promise 是 JS 的一个内置构造函数
const p = new Promise(回调函数)
new Promise 得到的对象 我们叫做promise对象
promise 对象上有一些方法:
then 方法(会在 promise 状态成功的时候执行)
catch 方法(会在 promise 状态失败的时候执行)
const p = new Promise(function (resolve, reject) {
/**
* 形参名无所谓
* 第一个形参:内部的值是一个函数,调用之后可以将当前这个 promise 的状态设置为成功
* 第二个形参:内部的值是一个函数,调用之后可以将当前这个 promise 的状态设置为失败
*/
// 书写我们的异步代码
const timer = Math.ceil(Math.random() * 3000)
console.log('班长,去帮我买瓶水')
setTimeout(() => {
if (timer > 1500) {
console.log('买水失败,用时:超时', timer)
// failure()
reject()
} else {
console.log('买水成功,用时:', timer);
// success()
resolve()
}
}, timer)
})
// console.log('打印变量p', p);
p.then(() => { console.log('如果我这行内容打印,说明 promise 的状态为成功'); })
p.catch(() => { console.log('如果我这行内容打印,说明 promise 的状态为失败'); })
promise 升级1
// 将promise封装到函数中
function fn() {
const p = new Promise(function (resolve, reject) {
const timer = Math.ceil(Math.random() * 3000)
console.log('班长,去帮我买瓶水')
setTimeout(() => {
if (timer > 1500) {
console.log('买水失败,用时:超时', timer)
reject('买水失败是因为时间超过了1500毫秒')
} else {
console.log('买水成功,用时:', timer);
resolve('买水成功是因为时间小于1500毫秒')
}
}, timer)
})
return p
}
// 得到 fn 函数内部的 promise 对象
const res = fn()
// 普通调用
// res.then(() => { console.log('如果我这行内容打印,说明 promise 的状态为成功'); })
// res.catch(() => { console.log('如果我这行内容打印,说明 promise 的状态为失败'); })
// 链式调用 (成功执行 then 失败执行 catch)
res.then((str) => { console.log('如果我这行内容打印,说明 promise 的状态为成功', str); }).catch((str) => { console.log('如果我这行内容打印,说明 promise 的状态为失败', str); })
promise 升级2
// 将promise封装到函数中
function fn() {
const p = new Promise(function (resolve, reject) {
const timer = Math.ceil(Math.random() * 3000)
console.log('班长,去帮我买瓶水')
setTimeout(() => {
if (timer > 1500) {
// console.log('买水失败,用时:超时', timer)
reject('超时,买水失败')
} else {
// console.log('买水成功,用时:', timer);
resolve('没有超时,买水成功')
}
}, timer)
})
return p
}
// 得到 fn 函数内部的 promise 对象
const res = fn()
/**
* 当你在第一个 then 里面返回一个新的 promise 对象
* 然后你可以在第一个 then 的后面,再次书写一个 then
*/
// 链式调用 (成功执行 then 失败执行 catch)
//链式调用的出现是为了解决调用地狱的问题
res.then((str) => {
console.log(`因为${str},所以奖励班长10个bug`)
return fn()
}).then((str) => {
console.log('如果我输出了,表示班长第二次买水成功')
return fn()
}).then((str) => {
console.log('如果我输出了,表示班长第三次买水成功')
}).catch((str) => {
console.log(`如果我输出了,说明之前某一次买水失败了`)
})
// .catch 必须放在最后
二、async 与 await
async 与 await是 promise 的一种调用方案(也就是说必须结合着 promise 一起使用)
-
作用:能够将异步代码写的像“同步代码”一样
-
async:书写在一个函数的开头,表明当前函数是一个异步函数,内部可以书写 await -
await:具有等待的含义,书写在异步函数前,代码运行到这个位置的时候,会有一个等待效果
一直等到这个异步任务结束,并且将异步任务的反馈结果当一个值返回出来
function fn() {
const p = new Promise(function (resolve, reject) {
const timer = Math.ceil(Math.random() * 3000)
console.log('班长,去帮我买瓶水')
setTimeout(() => {
if (timer > 1500) {
// console.log('买水失败,用时:超时', timer)
reject('超时,买水失败')
} else {
// console.log('买水成功,用时:', timer);
resolve('没有超时,买水成功')
}
}, timer)
})
return p
}
async function newFn() {
// 因为函数开头写了 async 所以这个函数就是一个独特的异步函数,内部可以书写 await
// await是等待的意思,他必须等待后边的 promise 结束后再往下继续执行代码
const r1 = await fn()
console.log('第一次买水:', r1);
const r2 = await fn()
console.log('第二次买水:', r2);
const r3 = await fn()
console.log('第三次买水:', r3);
}
newFn()
三、async 与 await 的缺点
不能正常的捕获到 promise 的失败状态
解决方法:
1. try ... catch
2.封装一个永远不会失败的 promise
-
也就是说,不管请求状态如何,永远调用
resolve让promise的状态一定是成功的 -
为了区分这次请求是成功还是失败,我们不再单纯的返回一个字符串了
-
而是返回一个对象
-
注意:这个对象,我们约定里面有一个
code属性,成功赋值时为1, 失败时赋值为0
function fn() {
const p = new Promise(function (resolve, reject) {
const timer = Math.ceil(Math.random() * 3000)
console.log('班长,去帮我买瓶水')
setTimeout(() => {
if (timer > 1500) {
// reject('超时,买水失败')
resolve({
code: 0, //0 代表当前请求失败
msg: '超时,买水失败'
})
} else {
// resolve('没有超时,买水成功')
resolve({
code: 1, //1 代表当前请求成功
msg: '没有超时,买水成功'
})
}
}, timer)
})
return p
}
// 方案2
newFn()
async function newFn() {
const r1 = await fn()
console.log(r1)
if (r1.code === 0) {
console.log('请求失败的补救措施')
} else {
console.log('请求成功,正常执行代码即可')
}
}
// 解决缺点 方案1
// newFn()
// async function newFn() {
// try {
// const r1 = await fn()
// console.log('第一次买水:', r1)
// } catch (error) {
// console.log(error)
// }
// console.log('我是一个字符串,我的执行并不会被打断')
// }
四、promise 的其他方法
function fn() {
const p = new Promise(function (resolve, reject) {
const timer = Math.ceil(Math.random() * 3000)
console.log('班长,去帮我买瓶水')
setTimeout(() => {
if (timer > 1500) {
reject('超时,买水失败')
} else {
resolve('没有超时,买水成功')
}
}, timer)
})
return p
}
// 1.promise 对象上的方法
// const res = fn()
// res.then(() => {
// console.log('成功时执行');
// }).catch(() => {
// console.log('失败时执行');
// }).finally(() => {
// /**
// * 正常业务场景中,我们在发起一个请求的时候,会将页面弹出一个遮罩层
// * 然后在请求结束的时候,需要将这个遮罩层关闭
// * 这个时候如果放在 then 中,那么会有一个问题,就是请求失败的时候不会触发 then
// * 所以我们一般不会放在 then 关闭遮罩层,而是放在 finally 中
// */
// console.log('每一次都会执行(不会考虑成功还是失败)');
// })
// 2.promise 构造函数上的一些方法
// Promise.all([fn(), fn(), fn()]).then(() => {
// console.log('所有的参数全部返回同一个成功状态的时候,会执行');
// }).catch(() => {
// console.log('所有参数中,有一个为失败状态,就会执行catch');
// })
// 3.
Promise.race([fn(), fn(), fn()]).then(() => {
console.log('这些参数中,结束最快的那一个状态为成功的时候执行');
}).catch(() => {
console.log('这些参数中,结束最快的那一个状态为失败的时候执行');
})
promise 的其他方法2
function fn() {
const p = new Promise(function (resolve, reject) {
const timer = Math.ceil(Math.random() * 3000)
console.log('班长,去帮我买瓶水')
setTimeout(() => {
if (timer > 1500) {
reject('超时,买水失败')
} else {
resolve('没有超时,买水成功')
}
}, timer)
})
return p
}
// Promise.allSettled([fn(), fn(), fn()]).then((res) => {
// /**
// * 在数组内传递的 promise 全都执行完毕后,返回一个数组给到 then 函数
// * 数组内的对象就是我们传递进来的 promise 对象的执行结果
// */
// console.log(res);
// })
Promise.resolve().then(() => {
console.log('强制返回一个状态为成功的 promise');
})
Promise.reject().then(() => {
console.log('如果我打印了,说明当前的promise 状态为成功');
}).catch(() => {
console.log('强制返回一个状态为失败的 promise 对象');
})