promise
-
其实 promise 就可以理解为一个盒子, 这个盒子内部 可以帮我们承载一些 需要一定时间才能加载完毕的代码 (其实就是异步代码)
-
刚书写一个 promise 内部的状态为 等待/持续
-
将来promise 会转变状态, 要么转为成功 要么转为失败
- 而且 promise 的状态一经转换, 永远不会在改变
- 等待 -> 成功
- 等待 -> 失败
-
利用 ES6 新增的一个内置构造函数 帮助我们创建
const p = new Promise(function (reslove, rejected) {
// * 当前回调函数 会接收两个形参
// * 第一个形参: 他的值是一个函数, 你可以调用, 调用完毕后, 可以修改 当前 promise 的状态为成功
// * 第二个形参: 他的值也是一个函数, 你也可以调用, 调用完毕后, 可以修改 当前 promise 的状态为失败
// *
// * 注意: 两个形参都是一个函数, 函数由 Promise 处理, 我们不需要处理, 我们只需要在合适的时机 调用即可
})
-
promise 实例化对象中, 有一个 方法 then 是一个函数
- 当前的 then 内部可以接收一个 回调函数
-
这个回调函数会在当前的 promise 实例化对象, 状态为成功的时候执行
-
还有另外一个方法 catch 也是一个函数, 内部可以接受一个 回调函数
- 这个回调函数会在当前的 promise 实例化对象, 状态为失败的时候执行
-
注意:
-
不管是 then 函数内部的 回调函数还是 catch 函数内部的回调函数
- 都可以接受一个参数
-
这个参数的值由在 promise 内部更改状态的函数 传递
-
这个参数也不是必须要有, 你需要的话可以用, 不需要可以不写
-
p.then((res) => {
console.log('当前的 promise 实例化对象 如果状态成功, 那么我会执行', res)
}).catch((err) => {
console.log('当前的 promise 实例化对象 如果状态失败, 那么我会执行', err)
})
function fn() {
return new Promise(function (reslove, rejected) {
const time = Math.floor(Math.random() * 3000) + 2000
setTimeout(() => {
if (time > 3000) {
rejected(time)
} else {
reslove(time)
}
}, time)
})
}
// 链式调用, 在一个 then 后 返回一个 新的 promise 实例化对象, 那么我们可以再添加一个 then 函数
p.then((res) => {
console.log('成功', res)
return fn()
}).then((res) => {
console.log('如果第二次成功, 那么我也会执行')
return fn()
}).then((res) => {
console.log('如果第三次成功, 那么我也会执行')
}).catch((err) => {
console.log('失败', err)
})
-
Promise就是一个用来存储数据对象
-
但是由于Promise存取的方式的特殊,所以可以直接将异步调用的结果存储到Promise中
-
对Promise进行链式调用时
-
后边的方法(then和catch)读取的上一步的执行结果
-
如果上一步的执行结果不是当前想要的结果,则跳过当前的方法
-
当Promise出现异常时,而整个调用链中没有出现catch,则异常会向外抛出
async 和 await
-
必须要结合 promise 一起使用
-
async 书写在一个函数的前边, 表明当前是一个异步函数
-
await 书写在 promise 实例化对象前, 有一个作用是 只有当前这个对象的状态确定, 才会往下一行运行
-
注意:
- async await 只能处理 promise 的成功状态, 不能处理失败状态
function post() { return new Promise(function (reslove, rejected) { const time = Math.floor(Math.random() * 3000) + 2000 setTimeout(() => { if (time > 30) { rejected(time) } else { reslove(time) } }, time) }) } // 原本的写法 // fn().then((res) => { // console.log(res, '成功') // }).catch((err) => { // console.log(err, '失败') // }) // 利用 async await 优化 async function fn () { const res = await post() console.log('第一次请求完毕的结果: ', res) const res_2 = await post() console.log('第二次请求完毕的结果: ', res_2) console.log('一定是上边的异步代码运行完毕后, 才会执行我这个代码') } fn() -
通过await调用异步代码时,需要通过try-catch来处理异常
function post() {
return new Promise(function (reslove, rejected) {
const time = Math.floor(Math.random() * 3000) + 2000
setTimeout(() => {
if (time > 3000) {
rejected('当前请求超时, 目前的时间是' + time)
} else {
reslove(time)
}
}, time)
})
}
async function fn() {
// 当前写法 不能处理 promise 的错误状态
// const res = await post()
// console.log('第一次请求完毕的结果: ', res)
/**
* 解决方案
* 推荐做法
*/
try {
// 如果当前分支的代码, 运行完毕没有报错, 那么直接结束
const res = await post()
console.log('第一次请求完毕的结果: ', res)
// 如果当前分支的代码运行完毕后, 有报错, 那么会阻断报错, 并且将错误信息, 传递给 catch 分支的形参
} catch (err) {
console.log(err, '如果我执行, 说明 第一次请求出现报错')
}
try {
// 如果当前分支的代码, 运行完毕没有报错, 那么直接结束
const res = await post()
console.log('第二次请求完毕的结果: ', res)
// 如果当前分支的代码运行完毕后, 有报错, 那么会阻断报错, 并且将错误信息, 传递给 catch 分支的形参
} catch (err) {
console.log(err, '如果我执行, 说明 第二次请求出现报错')
}
}
fn()
function sum(a, b) {
return new Promise(resolve => {
setTimeout(() => {
resolve(a + b)
}, 2000);
})
}
async function fn3() { try {
let result = await sum(123, 456)
result = await sum(result, 8)
result = await sum(result, 9)
console.log(result)
} catch (e) {
console.log(e,"出错了~~")
}
}
* async await 只能处理 promise 的成功状态, 不能处理失败状态
* 我们 async await 解决不了 promise 的失败状态
* 所以我们干脆将 promise 修改成 一定是成功状态
* 但是如果将 promise 修改成一定成功, 也会出现一个新的问题
*
* 我们在外部没有办法确定当前是成功还是失败
* 所以有一个 前端内部所有人的约定
* 我们在返回信息的时候, 通过一个对象返回
* 对象内部可以有多个属性, 其中有一个 属性叫做 code
*
* 如果 code === 0 代表失败
* code === 1 代表成功
*/
function post() {
return new Promise(function (reslove, rejected) {
const time = Math.floor(Math.random() * 3000) + 2000
setTimeout(() => {
if (time > 3000) {
// rejected('当前请求超时, 目前的时间是' + time)
reslove({
code: 0,
data: '当前请求超时, 目前的时间是' + time
})
} else {
reslove({
code: 1,
data: time
})
}
}, time)
})
}
async function fn() {
const res = await post()
console.log('请求完毕的结果: ', res)
if (res.code === 0) {
console.log('当前的请求失败了, 做一点措施, 并且不要再往下进行了')
return alert(res.data)
}
console.log('当前请求成功, 拿到数据后我们开始渲染页面')
}
fn()
promise的其他方法
Promise.resolve() 创建一个立即完成的Promise
Promise.reject() 创建一个立即拒绝的Promise
Promise.all([...]) 同时返回多个Promise的执行结果
其中有一个报错,就返回错误
Promise.allSettled([...]) 同时返回多个Promise的执行结果(无论成功或失败)
{status: 'fulfilled', value: 579}
{status: 'rejected', reason: '哈哈'}
Promise.race([...]) 返回执行最快的Promise(不考虑对错)
Promise.any([...]) 返回执行最快的完成的Promise 如果所有的Promise都失败才会返回一个错误信息。
// 构造函数上的方法
// 返回一个状态为 成功的 promise 实例化对象
Promise.resolve().then(() => {
console.log('一定执行 then')
}).catch(() => {
console.log('我不会执行')
})
Promise.resolve('随手传入一个参数').then((res) => {
console.log('一定执行 then', res)
})
// 返回一个状态为 失败的 promise 实例化对象
Promise.reject().then(() => {
console.log('我不会执行')
}).catch(() => {
console.log('我一定会执行')
})
Promise.reject('传入一个字符串 作为参数').catch(err => console.log(err))
//. 实例化对象上的方法
Promise.all([fn(), fn(), fn()]).then(() => {
// 数组中所有的 promise 实例化对象 全都成功的时候执行
console.log('成功')
}).catch(() => {
// 数组中 所有的 peomise 实例化对象, 有一个失败, 就执行
console.log('失败')
})
/**
* 当前方法接受一个数组, 数组内可以传入多个 promise 实例化对象
*
* 等全部执行完毕的时候, 会执行后续的 then 方法
*
* then 方法接受一个参数, 就是我们传入所有 promise 实例化对象运行的结果
*/
Promise.allSettled([fn(), fn(), fn()]).then((res) => {
console.log('执行结束', res)
}).catch(() => {
console.log('你看我会不会执行')
})
// race 会接收一个数组, 数组中可以传入若干个 promise 实例化对象, 其中状态出现最快的会影响当前的结果
Promise.race([fn(), fn(), fn()]).then(() => {
// 如果第一个出现结果的 promise实例化对象为成功, 就执行
console.log('成功')
}).catch(() => {
// 如果第一个出现结果的 promise实例化对象为失败, 就执行
console.log('失败')
})
Promise.any([
Promise.reject(1111),
Promise.reject(2222),
Promise.reject(3333),
]).then(r => {
console.log(r)
}).catch(r => {
console.log("错误", r)
})
fn()
.then(() => { console.log('成功') })
.catch(() => { console.log('失败') })
.finally(() => { console.log('不管结果成功还是失败, 都会执行') })
promise 内的 异步代码
-
- new Promise 内部 是按照 同步的顺序执行, 除非你写了异步代码 (定时器/请求相关的)
-
- promise 的实例化对象中的方法 (then/catch/finally) 这三个代码是按照异步任务执行
-
JS 代码运行流程, 从上往下运行
- 如果有异步任务
-
先执行一个 宏任务 (是把 JS 整体代码当作一个 宏任务)
-
然后在执行 微任务队列的清空操作
-
- 如果有异步任务