回调函数 callback
- 把函数A通过参数的形式传递给函数B,在函数B内部以形参的方式调用
- 函数A就叫函数B的回调函数
- 通常用到回到函数的场景都是在异步代码封装
-
// 1. 回调函数 function A () { console.log('函数A执行') } function B(cb) { console.log('函数B执行') cb() } B(A)
- 封装一个异步函数 一般都是网络请求
-
/** * 3. 因为网络请求, 可能会成功, 也有可能失败 * 约定: 时间超过 3500 算作失败, 否则算成功 */ function fn (chenggong, shibai) { console.log('班长帮我去买一瓶水') const timer = Math.ceil(Math.random() * 3000) + 2000 setTimeout(() => { if (timer > 3500) { console.log('班长买水失败', timer) shibai() } else { console.log('班长买水成功', timer) chenggong() } }, timer) }
fn(
() => {
console.log('谢谢班长辛苦了, 帮我退掉吧')
},
() => {
console.log('辛苦班长了, 买不到别回来了')
}
)
# 回调地狱
- 回调地狱并不是一个bug,而是一种代码格式,这种代码格式,不利于我们阅读
- 解决Promise(期约) 他是解决回调函数代码的
- ```javascript
function fn(chenggong, shibai) {
console.log('班长帮我去买一瓶水')
const timer = Math.ceil(Math.random() * 3000)
setTimeout(() => {
if (timer > 3500) {
console.log('班长买水失败', timer)
shibai()
} else {
console.log('班长买水成功', timer)
chenggong()
}
}, timer)
}
/**
* 需求:
* 1. 第一次买水成功以后, 再次执行一边, 但, 必须是第一次买水成功以后 才能再去买一瓶
*
* 2. 在第二次买水成功以后, 再次执行一边, 但, 必须是第二次买水成功以后 才能再去买一瓶
*/
fn(
() => {
console.log('谢谢班长辛苦了, 帮我退掉吧')
fn(
() => {
console.log('第二次买水成功')
fn(
() => {
console.log('第三次买水成功')
},
() => {
console.log('第三次买水失败')
}
)
},
() => {
console.log('第二次买水失败')
}
)
},
() => {
console.log('辛苦班长了, 买不到别回来了')
}
)
认识 Promise
- 一个新的异步代码封装方案 以前通过回调函数的形式去封装,会导致出现回调地狱 所以用Promise来解决
- promise有三个状态:
- 持续:pending
- 成功:fulfilled
- 失败:rejected
- promise只会发生两个转换
- 持续===>成功
- 持续===>失败
- promise实例化对象上有两个方法
- .then-----触发成功状态
- .catch----触发失败状态
- promise实例化对象可以接受链式调用
-
const p = new Promise(function (reslove, reject) { /** * resolve 会把我们这个 promise 状态转换为 成功 * reject 会把我们这个 promise 状态转换为 失败 * * 这两个 都是 函数 */ // 写上我们的异步代码 // console.log('班长帮我去买一瓶水') const timer = Math.ceil(Math.random() * 3000) + 2000 setTimeout(() => { if (timer > 3500) { console.log('班长买水失败', timer) reject('班长买水失败, 再换个地方买') } else { console.log('班长买水成功', timer) reslove('班长买水成功, 奖励二十个bug') } }, timer) }) // 变量p 就是 promise 的实例化对象 // console.log(p)
/**
* promise 实例化对象 上有两个方法
* .then----触发成功状态
* .catch-----触发失败状态
*/
// p.then(function (res) {
// /**
// * 再 promise 成功状态时触发
// * 并且接受 resolve时传递的参数(请看50行代码)
// */
// console.log('0000000000', res)
// })
// p.catch(function (res) {
// /**
// * 再 promise 失败状态时触发
// * 并且接受 reject 时传递的参数(请看46行代码)
// */
// console.log('11111111', res)
// })
/**
* promise 实例化对象 可以接受 链式调用
*/
p.then(function (res) {
console.log('0000000000', res)
}).catch(function (res) {
console.log('11111111', res)
})
# 封装Promise
```javascript
function fn() {
const p = new Promise(function (reslove, reject) {
const timer = Math.ceil(Math.random() * 3000) + 2000
setTimeout(() => {
if (timer > 3500) {
reject('班长买水失败, 再换个地方买')
} else {
reslove('班长买水成功, 奖励二十个bug')
}
}, timer)
})
return p
}
/**
* 再promise.then 内部 return 了一个 新的 promise对象
* 可以在后续再写一个 .then
*/
fn().then((res) => {
console.log('班长第一次买水成功')
return fn()
}).then((res) => {
console.log('班长第二次买水成功')
return fn()
}).then((res) => {
console.log('班长第三次买水成功')
}).catch((res) => {
console.log('班长买水失败')
})
async 和 await
- 能帮助我们把异步代码,写的和同步代码一样
- 函数开头必须写async表面内部可以书写await
-
function fn() { const p = new Promise(function (reslove, reject) { const timer = Math.ceil(Math.random() * 3000) setTimeout(() => { if (timer > 3500) { reject('班长真好, 就是买水失败, 再换个地方买') } else { reslove('班长真好, 就是买水成功, 奖励二十个bug') } }, timer) }) return p } // 1. 函数开头必须书写 async 表明内部可以书写 await async function newFn() { /** * await 后边需要跟着 promise * await 表示等到的意思, 执行到 fn() 虽然是异步的 * 但是因为有 await 关键字, 此时不会往下继续执行, * 而是等待 fn() 执行完毕, 在往下执行 */ let r1 = await fn() console.log(r1) // console.log('如果失败了, 执行这行代码, 提示用户网络有问题') } newFn() // promise 常规写法 // fn().then((res) => { // console.log('班长第一次买水成功') // })
async 和 await 语法的缺点
-
m没有办法捕获到错误,只能接受promise的成功状态 如果报错,会中断程序执行
-
解决方法1:try.....catch
-
function fn() { const p = new Promise(function (reslove, reject) { const timer = Math.ceil(Math.random() * 3000) + 4000 setTimeout(() => { if (timer > 3500) { reject('班长真好, 就是买水失败, 再换个地方买') } else { reslove('班长真好, 就是买水成功, 奖励二十个bug') } }, timer) }) return p } /** * 解决方法1: try...catch * * 首次执行的时候 会走 try 这个分支, 如果这个位置有报错 * 他会结束执行 try 分支, 然后走 catch 分支 * * 如果再运行 try 分支的时候, 没有报错, 那么 catch 不会运行 */ async function newFn() { try { let r1 = await fn() console.log(r1) } catch (error) { console.log(error) console.log('如果失败了, 执行这行代码, 提示用户网络有问题') } } newFn() -
解决方法2:更改promise的封装
-
/** * 解决方法2: 更改 promise 的封装 * * 原因: promise 执行 reject 时 async await 不能捕获到错误, * * 解决: 让这个 promise 不管什么情况 都返回 resolve * 我们通过 返回的 参数, 区分现在时成功还是失败 * * * 开发中 对象内的 code 如果为0, 一般代表失败 * 对象内的 code 如果为1, 一般代表成功 */ function fn() { const p = new Promise(function (reslove, reject) { const timer = Math.ceil(Math.random() * 3000) + 1000 setTimeout(() => { if (timer > 3500) { // reject('班长真好, 就是买水失败, 再换个地方买') reslove({ code: 0, msg: '班长真好, 就是买水失败, 再换个地方买' }) } else { reslove({ code: 1, msg: '班长真好, 就是买水成功, 奖励二十个bug' }) } }, timer) }) return p } async function newFn() { let r1 = await fn() if (r1.code === 0) { console.log('您的网络有问题') } else { console.log(r1.msg) } } newFn()