第二十八章 回调函数和Promise

一、回调函数(callback)

1、含义:是一种函数的调用方式

2、用于: 用于我们的代码的封装,一般多用于 异步代码的封装

  • 异步代码: 定时器(一次性定时器 , 反复性定时器)
  • 我们封装异步代码的时候 想要在结尾做一些事情的时候
  • 这个时候我们一般使用的回调函数
  • 运动函数: 结束的末尾做一些事情 , 使用的是函数

3、为什么要使用回调函数

  • 因为我们的函数可以承载一段代码
  • 就是我们的函数的即时调用性

4、什么是回调函数

  • 就是把一个函数但是形参传递到另一个函数中
  • 在另一个函数中以形参的方式调用
  • 我们就把这个函数叫做另一个函数的回调函数
  • 我们把一个函数A 当做形参传递到另一个函数B中
  • 在函数B中以形参的方式调用这个函数
  • 这个时候我们把 A函数称之为B函数的回调函数

二、回调地域

1、含义:回调函数的嵌套使用就会形成回调地狱

2、影响:代码的维护性不高以及代码的阅读性不高

3、解决办法:在ES6中出现一种新的封装异步代码的方式 ,叫做: Promise

  • 是一种异步代码的封装方案
  • 因为换了一种封装方案, 不需要按照回调函数的方式去调用
  • 需要按照 Promise 的形式去调用

三、Promise

1、Promise的三个状态

  • 持续: pending 一种持续的状态
  • 成功: fulfilled 从持续到成功
  • 失败: rejected 从持续到失败

2、Promise的两种状态转换

  • 从 持续状态 转化到 成功
  • 从 持续状态 转化到 失败

3、Promise的基础语法

  • ES6 内的 内置构造函数
  • 语法:const p = new Promise(function () {书写你异步的事情})
  • p 就是一个 promise 实例对象
  • promise 对象可以触发两个方法

=>p.then(函数) 成功的时候执行then()里面的函数

=>p.catch(函数) 失败的时候执行catch()里面的函数

=>这两个方法只是注册一个 成功 或者 失败 的时候会执行的函数

// Promise基础语法
const p = new Promise(function (resolve, reject) {
  // 第一个形参: 是一个状态转换函数, 当你调用的时候, 会把当前 Promise 的状态转换为 成功
  // 第二个形参: 是一个状态转换函数, 当你调用的时候, 会把当前 Promise 的状态转换为 失败
  const time = Math.random() * 3000 + 2000
  setTimeout(() => {
    if (time < 3500) {
        // console.log('失败')
        // 表示本次 承若失败了, 我应该把本次 Promise 状态转换为失败
        reject('打成猪头')
    } else {
        // console.log('成功')
        // 表示本次 承若成功了, 我应该把本次 Promise 状态转换为成功
        resolve('三珍海味')
    }
  }, time)
})
// 触发两个函数
p.then(function (address) {
    // 当前函数会在本次 Promise 转换为成功的时候 触发
    console.log('请你吃大餐', address)
})
p.catch(function (address) {
    // 当前函数会在本次 Promise 转换为失败的时候 触发
    console.log('我要揍你',address)

4、Promise的进阶语法

  • 当你在第一个 then 里面返回(return) 一个新的 Promise 对象的时候
  • 可以在第一个 then 后面继续第二个 then
jiehun()
    .then(function (address) {
        console.log('第一次 : ', '请你吃大餐', address)
        return jiehun()
    })
    .then(function (address) {
        console.log('第二次 : ', '请你吃大餐', address)
        return jiehun()
    })
    .then(function (address) {
        console.log('第三次 : ', '请你吃大餐', address)
    })
    .catch(function (address) {
        // 当前函数会在本次 Promise 转换为失败的时候 触发
        console.log('我要揍你',address)
    })

四、async和await

1、是两个关键字

  • 因为Promise解决回调地狱依旧不够友好
  • 所以出现了async 和 await
  • async: 异步的意思
  • await: 等待的意思

注意: (1)需要配合的必须是 Promise 对象

·(2)Promise 语法的调用方案

意义: 把 异步代码 写的看起来 同步代码

2、async

  • 直接书写在函数的前面, 表示该函数是一个 异步函数
  • 只是把这个函数叫做异步函数 , 但是不改变这个函数本来的性质
  • 也就是说之前是同步函数依旧是同步函数
  • 之前是异步函数依旧是异步函数
  • 意义: 表示在该函数内可以使用 await 关键字

3、await

  • 必须书写在一个有 async 关键字的函数内
  • await 后面等待的内容必须是一个 promise 对象
  • 本该使用 then 接受的结果, 可以直接定义变量接受了

4、使用

// 按照 Promise 的形式封装代码
function jiehun() {
    // Promise
    const p = new Promise(function (resolve, reject) {
    const time = Math.random() * 3000 + 2000
        setTimeout(() => {
            if (time < 3500) {
                // console.log('失败')
                // 表示本次 承若失败了, 我应该把本次 Promise 状态转换为失败
                reject('打成猪头')
            } else {
                // console.log('成功')
                // 表示本次 承若成功了, 我应该把本次 Promise 状态转换为成功
                resolve('三珍海味')
            }
        }, time)
    })
    // 把本次 promise对象 返回出去
    return p
}
// 通过async 和 await来使用
async function fn() {
    // await 是等待的意思
    // 在当前 fn 函数内, await 必须要等到后面的 Promise 结束以后, 才会继续执行后续代码
    const r1 = await jiehun()
    console.log('第一次 : ', r1)
    const r2 = await jiehun()
    console.log('第二次 : ', r2)
    const r3 = await jiehun()
    console.log('第三次 : ', r3)
}
fn()

五、Promise封装解决失败报错

因为我们上面的封装只能走成功,一旦报错就会阻止后续代码的执行,以下是解决办法

// 按照 Promise 的形式封装代码
function jiehun() {
  // Promise
  const p = new Promise(function (resolve, reject) {
    const time = Math.random() * 3000 + 2000
      setTimeout(() => {
            if (time < 4500) {
                // console.log('失败')
                // 表示本次 承若失败了, 我应该把本次 Promise 状态转换为失败
                reject('打成猪头')
            } else {
                // console.log('成功')
                // 表示本次 承若成功了, 我应该把本次 Promise 状态转换为成功
                resolve('三珍海味')
            }
        }, time)
    })
  // 把本次 promise对象 返回出去
  return p
}
// 使用 async 和 await 的方式来调用
// async 和 await 语法的缺点
// await 只能捕获到 Promise 成功的状态
// 如果失败, 会报错, 终止程序继续执行
async function fn() {
    const res = await jiehun()
    console.log(res)
    console.log('后续代码继续执行, 提示用户网络请求失败')
}
fn()
// 解决方案1: 使用 try catch 语法
// 语法: try { 执行代码 } catch(err) { 执行代码 }
// 首先执行 try 里面的代码, 如果不报错, catch 的代码不执行了
// 如果报错, 不会爆出错误, 不会终止程序执行, 而是执行 catch 的代码, 把错误信息给到 err 参数
try {
    console.log(abc)
} catch (err) {
    console.log('前面的代码失败了')
    console.log(err)
}
async function fn() {
    try {
        const res = await jiehun()
        console.log(res)
    } catch (err) {
        console.log('后续代码继续执行, 提示用户网络请求失败')
    }
}
fn()
// 解决方案2: 改变封装的思路
// 原因: 因为 promise对象 有成功状态 和 失败状态
//      所以会在失败状态的时候, 报错
// 解决: 我就让当前的 Promise 对象百分百成功
// 让成功和失败都按照 resolve 的形式来执行
//   只不过传递出去的参数, 记录一个表示成功或者失败的信息
function jiehun() {
const p = new Promise(function (resolve, reject) {
    const time = Math.random() * 3000 + 2000
    setTimeout(() => {
        if (time < 3500) {
        resolve({ code: 0, message: '打成猪头' })
        } else {
        resolve({ code: 1, message: '三珍海味' })
        }
    }, time)
})
// 把本次 promise对象 返回出去
return p
}
// 使用的时候, 因为该 Promise 没有失败状态. 所以放心使用 awiat
async function fn() {
    const r1 = await jiehun()
    console.log(r1)
    if (r1.code === 0) {
        console.log('失败的逻辑, 提示用户错误')
    }
    const r2 = await jiehun()
    console.log(r2)
    if (r2.code === 0) {
        console.log('失败的逻辑, 提示用户错误')
    }
    const r3 = await jiehun()
    console.log(r3)
    if (r3.code === 0) {
        console.log('失败的逻辑, 提示用户错误')
    }
}
fn()

六、Promise其他方法

1、then() 是成功的时候要执行的方法

2、catch() 是失败的时候要执行的方法

3、其它方法

=>finally() 完成的时候执行的方法

  • 不管是成功还是失败都是成功
  • 这个一般多使用在loading效果的时候
// 按照 Promise 的形式封装代码
function jiehun() {
    // Promise
    const p = new Promise(function (resolve, reject) {
    const time = Math.random() * 3000 + 2000
        setTimeout(() => {
            if (time < 3500) {
                // console.log('失败')
                // 表示本次 承若失败了, 我应该把本次 Promise 状态转换为失败
                reject('打成猪头')
            } else {
                // console.log('成功')
                // 表示本次 承若成功了, 我应该把本次 Promise 状态转换为成功
                resolve('三珍海味')
            }
        }, time)
    })
    // 把本次 promise对象 返回出去
    return p
}
// finally()
jiehun()
    .then(res => console.log(res))
    .catch(err => console.log(err))
    .finally(function () {
        console.log('当前 Pomise 完成了')
    })

七、Promise本身方法

1、all():

  • 作用: 可以同时触发多个 Promise 行为
  • 只有所有的 Promise 都成功的时候, all 才算成功
  • 只要任何一个 Promise 失败的时候, all 就算失败了
  • 语法: Promise.all([ 多个promise ])

2、race():

  • 作用: 可以同时触发多个 Promise 行为
  • 按照速度计算, 当第一个结束的时候就结束了
  • 语法: Promise.race([ 多个promise ])

3、allSettled():(ES2020)

  • 作用: 可以同时触发多个 Promise 行为
  • 不管多个成功还是失败, 都会触发
  • 会在结果内以数组的形式给你返回每一个 Promise 行为的成功还是失败

4、resolve():

  • 强行返回一个成功状态的 promise对象

5、reject():

  • 强行返回一个失败状态的 promise对象
// all()
Promise
    .all([jiehun(),jiehun(),jiehun()])
    .then(res => console.log(res))
    .catch(err => console.log(err))
        
// race
Promise
    .race([ jiehun(), jiehun(), jiehun() ])
    .then(res => console.log(res))
    .catch(err => console.log(err))
// allSettled
Promise
    .allSettled([ jiehun(), jiehun(), jiehun(), jiehun(), jiehun(), jiehun() ])
    .then(res => console.log(res))
// resolve()
Promise.resolve('成功').then(res => console.log(res))
// reject()
Promise.reject('失败').catch(err => console.log(err))

八、事件轮询

1、时间:从开始执行代码就开始执行轮询

2、规则

  • 从 宏任务开始
  • 每执行完毕 一个 空任务, 清空一次微任务队列(不管微任务队列内有多少任务, 都执行完毕)
  • 再次执行一个 宏任务
  • 循环往复, 直到所有任务队列清空结束

3、关键词

  • 单线程: JS 是一个单线程的代码执行机制, 逐行依次执行代码, 会阻塞代码执行
  • 调用栈: 用来执行代码的, 所有的代码进栈执行, 执行完毕出栈
  • 队列: 用来存放异步任务的, 先进先出
  • 宏任务队列: JS整体代码, setTimeout, setInterval, ...
  • 微任务队列: Promise.then(), ...
  • 轮询: 轮流询问 宏任务 和 微任务队列的任务来执行

注意: WEBApi, 用来负责提供异步机制, 计时, 分配任务去到指定队列

九、数组扁平化

1、就是把多维的数组转成一位的数组格式

const arr = [1, 2, [3, 4, [5, 6, [7, 8, [9,]]]]]
// res = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
// 方案1:
// 准备一个新数组
// 遍历原始数组, 依次插入到新数组内
// 因为不确定深度有多少层, 需要递归实现
function flat(origin) {
    // 准备一个新数组
    const ary = []
    // 递归遍历原始数组
    function fn(origin) {
        origin.forEach(item => {
            // 判断 item 如果是一个数组 - 递归
            if (item.constructor === Array) {
                // 把 item 内的所有内容拆开插入到 ary 内
                fn(item)
                // 判断 item 如果不是一个数组 - 插入 ary
            } else {
                ary.push(item)
            }
        })
    }
    // 把 origin 内的所有内容拆开插入到 ary 内
    fn(origin)
    // 返回 ary
    return ary
}
// 使用的时候
// res 接受的就是一个扁平后的数组
const res = flat(arr)
console.log(res)
// 方案2: 利用 toString()
// 缺点: 尽量操作的全都是基本数据类型
//   数组.toString() 就是把数组的中括号全部去掉
// 注意: 这个方法的返回值是一个字符串, 需要再次使用 split() 拆开
const res1 = arr.toString().split(',')
console.log(res1)
// 方案3: 利用 flat 方法
// ES6 提供的数组常用方法
// 语法: 数组.flat(数字)
//   数字: 表示拆开多少层
const res2 = arr.flat(Infinity)
console.log(res2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值