JS期约和异步

期约和异步函数

异步编程

1: 异步执行的函数需要在更新x的值以后通知其他代码。 —》如果程序不需要这个值,那就只管继续执行,不必等待这个结果

2:早期的异步编程模式

​ 在早期的JS中,只支持定义回调函数 来表明异步操作 完成

​ —串联多个异步操作是一个常见的问题,通常需要深度嵌套的回调函数(回调地狱)

术语:

​ 消息队列

​ 回调函数

​ 调度异步操作

嵌套异步回调

​ 异步返回值 又 依赖 另一个 异步返回值

—这种是较为老套的异步操作

​ 原里为:

​ 函数(A)封装 一个 可以调用回调函数的函数(比如setTimeout())

​ A接收三个参数 (value, success,failure) 值,成功的回调函数的引用,失败的回调函数的引用

​ 在setTimeout的回调函数里面

​ 调用 success 或 failure

​ —这种缺点很明显:

​ 在初始化的时候需要 提前定义 好 success 和 failure 在 setTimeout的回调函数里面的 “行为”–》也就是需要预先指定

​ —切记 success–》指向 传递进来的 函数—》调用success 等于调用外面的函数

​ — 根据传递的函数引用不同,再 setTimeout 的回调函数里面调用 success 会产生不同的行为

2:—而嵌套调用

​ 就是在 success 或 failure 里面再调用 A(封装函数)

期约

相关名词

1:执行器 executor -->在创建新期约时 需要传入 执行器函数 作为参数 (不提供这个函数 就会抛出错误 SyntasError)

​ 1: 初始化期约的异步行为

​ 2: 控制状态的最终转换

​ --执行器函数 有两个参数 resolve 和 reject (通常是这个名字嘛)、

​ 在期约的执行器函数中: 调用 resolve() --> 期约的状态转化为 resolved

​ 调用 reject() -->期约的状态转化为 rejected -->会抛出一个错误

​ 例子 new Promising( (resolve,reject)=>{

​ resolve(); --》在调用这个 resolve() 之后 期约状态转为 resolved

​ })

​ 3: 执行器函数是同步执行的, 因为执行器函数是期约的初始化程序

2: Promise 引用类型

1:期约状态机

​ 1:期约是一个有状态的对象, 一个实例化后的期约对象 , 可能处于以下三种状态之一

​ 1:pending 待定

​ 2: fulfilled/resolved 兑现/解决

​ 3: rejected 拒绝
2:期约状态是私有的,不能通过js 检测到

待定

​ 1:pending 是期约的最初始状态.

​ 在这个状态下: 期约可以转换

​ 1:pending–>fulfilled

​ 2: pending -->rejected

​ 3: pending–>pending 保持 待定状态

​ —期约的状态的落定 是不可逆转的。

​ ----期约的状态转化 只可以从 pending 开始。

​ 且每个期约 可以转化 0 或 1 次

期约的作用

​ 主要的两大用途

​ 1:期约 抽象地表示一个 异步操作。

​ 2: 期约的状态 表示期约(异步操作) 是否完成。

​ —期约封装的异步操作会实际生成某个值, 程序在期约状态改变的时候,可以拿到这个异步操作生成的 值

​ 期约状态 切换为兑现 --》 就会有一个私有的内部值

​ 期约状态 切换为拒绝–》 就会有一个私有的内部理由

​ --所以在调用 reject() 的时候 不仅会切换 状态,还会抛出一个错误–>这个错误后面可以接收处理

通过执行函数控制期约状态

​ 1: 由于期约状态是"私有", 所以只能在内部操作–》影响 这个 “状态”

​ 2: 内部操作在 “期约的执行器函数” 中完成

promise.resolve()

​ 1:期约并非一开始 就必须处于 待定状态, 然后通过执行器函数才能转化为螺钉状态

​ 2: 可以将任何值 转化为一个期约

3: 这个解决期约的值 对应着 传给 promise.resolve() 的第一个参数, 多余参数自动忽略

4: promise.resolve() 是一个幂等方法

​ 如果传入的参数是一个期约, 那么它的行为就 类似于一个空包装

5:—幂等性 --》会保留传进来的期约的状态

promise.reject()

​ 1: 会实例化一个拒绝的期约 并抛出一个异步的错误 (这个错误 不能通过try/catch捕获,只能通过拒绝程序捕获)

​ ----因为 try catch 是同步处理, 拒绝的期约抛出的错误 需要 异步模式捕获

2: 没有幂等性, 如果给它 传入一个期约对象, 这个期约 会成为 它返回的 拒绝期约的理由

问题: promise.resolve() 和 promise.reject() 的作用是什么? 为什么要让期约状态直接落定?

  将值包装为一个期约,这个 值为  期约的值,
  然后在 .then 里面使用这个期约的值?
  
  答案:
  相当于  为什么使用true或者false的时候直接打字,而不是new Boolean(true)
  new Promise()时候会初始化,相当于前置任务,根据前置任务完成情况选择处理方式
  promise.resovle() 一般 是 结合着  new promise(执行器函数) 使用的
  例子1:如果,是一种只想顺序执行,借用.then链的简单异步,就可以用promise.resovle().then.then..来执行
  例子2:如果是发起请求,就得考虑失败处理  必须得 new promise
  也就是 直接获得一个 promise的实例,对这个实例 使用.then链式调用 进行简单的异步
  原话: “直接Promise.resolve,我就没用过”

期约的异步特性

​ 期约是同步对象(在同步执行模式中使用), 但 期约也是 异步执行模式的 “媒介”

1: 拒绝期约抛出的错误 没有抛到同步执行代码的线程里, 而是通过浏览器异步消息队列 来处理的

​ 因此: try/catch 块 不能捕获 该 异步错误 —》毕竟 try/catch 存在于同步线程,处理同步代码(同步错误)

​ —重点:

​ 代码一旦开始 以 异步模式执行, 则 唯一与之交互的方式 就是 使用异步结构

​ ----------也就是: 使用 期约的方法 --》处理异步代码 !

​ —所以综上: promise.reject() 返回的异步错误,只能用期约的处理程序 处理

期约的实例方法

​ 定义: 期约实例的方法 是连接外部同步代码 和 内部异步代码之间的桥梁

​ 这些方法: 可以访问异步操作 返回的数据,处理期约成功和失败的结果,

​ 可以连续对期约求值,添加只有期约进入终止状态时才会执行的代码

1:Thenable接口

​ 在ECMAScript 暴露的异步结构中,任何对象都有一个 then() 方法。 --》这个方法 实现了 Thenable接口

​ 注意: Thenable接口 不是 Promise专属的。 Promise类型 实现了thenable接口

1:promise.prototype.then()

相关名词:

​ 处理程序 : then()方法 接收两个参数 onResolved 处理程序 和 onRejected 处理程序

​ 期约进入 兑现 或 拒绝状态时 会执行 onResolved 处理程序 或 onRejected 处理程序

1:promise.prototype.then() 是为期约实例添加处理程序的主要方法

2:then() 的参数情况

​ 1:传给then() 任何非函数类型的参数 都会被静默忽略

​ 2: 如果指向提供onRejected参数, 那么最好在 onResolved 参数的位置上传入 undefined --这样有助于避免在内存中创建多余对象

3:then() 返回值 --一个新的期约实例

1:基于onResolvedc处理程序

​ 这个实例基于onResolved 处理程序的返回值构建。 —》该处理程序的返回值 会通过 Promise.resolve() 包装来生成新期约

​ 2: 如果没有提供 onResolved这个处理程序 则Promise.resolve() 就会包装上一个期约解决之后的值

​ —详细: 就是如果 期约调用then() 的时候 不传处理程序: 则: let p2 = p1.then() === let p2 = promise.resove(p1的期约的值) -->原样向后传递

​ 3: 如果onResolved() 这个处理程序中 没有 显示的返回语句,则 Promise.resolve() 会包装默认的返回值 undefined

​ 4: 在处理程序中抛出异常 就会返回一个拒绝的期约

​ 1:throw 抛出异常 —》throw抛出之后 如果没有catch接住,后面的代码就会跳过

​ 切记:此处是 异步抛出异常 --》不可能用catch接住,只能 用 onResoved 或.catch 处理 异步异常

​ 2: 返回错误值

​ – return Error(“bar”)

​ 不会触发 拒绝行为, 而是返回一个 基于该错误 包装的 “解决”的期约中

​ —也就是返回的是一个状态为 “解决的期约” 而非 throw异常抛出 返回的 “拒绝的期约”

2: 基于onRejected处理程序

​ onRejected 在捕获错误之后 一般是返回一个解决的期约

​ -----这个处理程序 返回期约的规则 和 onResolved 几乎差不多

​ 只不过这个处理程序 用来处理 “拒绝”的期约 ----》 切记: 抛出错误 会返回一个 “拒绝“的期约、

​ onResolved 用来处理 "解决"的期约

promise.prototype.catch()

​ 1: 这个方法用于 给期约添加拒绝处理程序 ----这个方法只接受一个参数 onRejected 处理程序

​ 这个方法相当于—语法糖: 调用它 相当于调用 Promise.prototype.then(null,onRejected)

—所以目前 如果期约抛出错误,有两个方法处理
1:onRejected 处理程序 处理错误
2:Promise.catch(onRejected) 方法 处理错误 -->接收onRejected作为参数

传递解决值和拒绝理由

1:状态落定后, 期约会提供其解决值 (或兑现) 或 其拒绝理由 给 相关状态的 处理程序

2: 拿到返回值后,就可以对 期约提供的值/拒绝理由 进行操作

​ 期约提供的值 -->在 resolve(A) rejected(B) --> 这两个返回 —》onResolve 和 onReject 这两个处理程序

​ —这个值 也叫: 期约的值 Promise < 状态 >: 值

4:Promise.prototype.finally()

​ 1:给期约添加onFinally处理程序 --》在期约转换为 解决 或 拒绝状态时 都会执行

​ —可以避免 onResolved 和 onRejected 处理程序中出现冗余状态

​ —onFinally 无法 知道期约的状态是解决还是拒绝

​ --所以 主要用于添加清理代码

​ 2:返回一个新的期约

​ 3: 与状态无关的方法

​ 大多数情况下 它将表现为 父期约的传递

​ —finally 可以和 new Promsie() 一起使用,当状态转化之后 触发 finally

5:非重入期约方法

​ 1:当期约进入落定状态时,与该状态相关的处理程序仅仅会被排期, 而非立即执行

​ 2:跟在添加这个处理程序的代码 之后的 同步代码 一定会在处理程序之前先执行

–1:在解决期约上调用then() 会把 onResolved 处理程序推进消息队列

​ 但这个处理程序 在当前线程上的同步代码执行完成前不会执行 —因此跟在then() 后面的同步代码一定先于处理程序执行

–2: 即使期约状态变化发生在添加处理程序之后,处理程序也会等到运行的消息队列让它出列是 才会执行

期约连锁与期约合成

1 :期约连锁

​ --把期约逐个地 串联起来

​ —每个期约实例的方法 都会返回一个新的期约对象,而这个新期约又有自己的实例方法。这样连缀方法调用就可以构成所谓的 “期约连锁”

​ 1:相对同步任务 —> 返回期约的时候 期约状态的落定是“同步”代码 (then() 处理程序里面的代码都是 异步微任务! 所以“相对”)

​ 2: 串行化异步任务 --》 返回期约的时候 then()里面的异步代码 决定返回期约的状态 --》期约状态落定 才能触发下一个 .then() 任务

1:一个期约可以有 任意多个 处理程序

2: 所以期约连锁可以构建 有向 非循环图的结构

2: 期约合成

​ Promise.all() 和 Promise.race() 方法

​ -----Promise类提供的将多个期约实例 组合成一个期约的静态方法

​ —合成后期约的行为 取决于内部期约的行为

1:promise.all()

​ promise.all() 静态方法创建的期约 会在一组期约全部解决之后再解决 。

​ 这个静态方法接收一个可迭代对象,返回一个新期约

​ 1:课迭代对象中的元素 会通过Promise.resolve() 转换为期约

​ 2: 合成的期约 只会在 每个包含的期约都解决之后 才解决

​ 3:有一个包含的期约待定,则合成的期约也会待定

​ 4: 有一个包含的期约拒绝,则合成的期约也会拒绝

​ 5:如果合成的期约解决, 则其解决值 就是包含期约解决值 的数组

​ 6:如果合成的期约拒绝,则第一个拒绝的期约 的拒绝理由 会作为 合成期约的拒绝理由。 之后的…不会再影响最终期约的拒绝理由

2:Promise.race()

​ 1:返回一个包装期约,是一组集合中最先解决或拒绝的期约的镜像。

​ 2:接收一个课迭代对象,返回一个 新期约

​ 3: 第一个状态落定的期约 , 就会包装其解决值 或 拒绝理由 返回相应的新期约 (解决/拒绝)

异步函数

特性:

​ 用async关键字 声明异步函数 --》可以用在: 函数声明,函数表达式,箭头函数 和 方法上

1:异步函数 始终返回期约对象。 —》利用Promise.resolve() 包装函数 return 的返回值

2: 异步函数具有异步特征, 但是仍然具有 js函数的正常行为

​ 在没有 await 关键字之前, 代码仍然是 同步

3: 在异步函数中抛出错误 会返回拒绝的期约

​ 拒绝期约的错误不会被异步函数捕获

2:await关键字

​ 使用await 关键字可以暂停异步函数代码的执行,等待期约解决

​ —期约解决 --》得到期约的解决的值

​ 概念:

​ 解包 — 尝试“解包”对象的值

​ await关键字尝试 “解包”对象的值,然后将这个值传给表达式,再异步恢复异步函数的执行

1: await 后面跟的对象

​ await期待一个实现 thenable接口的对象 -------注意:实现thenable接口的对象 不一定是 promsie

​ 1:实现thenable 接口的对象 被await"解包" ----》也就是 有 then() 这个方法 的对象, —疑惑: 什么是解包

​ 2: 未实现 ,则把这个值 当作已经解决的期约 —》 (获取这个已经解决的期约的值

2: await 等待会抛出错误的同步操作, 会返回拒绝的期约

3: await + 拒绝期约 可以被异步函数捕获

​ 例如:

​ async function foo(){

​ await Promise.reject(3);

​ }

foo().catch((x)=>{

​ console.log(x);

})

-----这样 异步函数foo 会返回一个 拒绝的期约 然后可以利用 .catch() 异步函数捕获错误的方法 处理报错

3:await的限制

​ 1:await关键字必须在异步函数中使用 await和async是一对的

​ 2: 异步函数的特质 不会扩展到嵌套函数‘

​ 以对 async await 只会影响一层函数

4:停止和回复执行函数

​ 1:js运行时 在碰到await关键字时候,会记录在哪里暂停执行

​ 等到 await 右侧的值可用 ----》什么叫“可用” (不立即可用的值 例如 期约的值)

​ js运行时 会向消息队列中推送一个任务,这个任务会恢复异步函数的值

​ – 因此: 就算 await 后面 跟着一个立即可用的值(例如常数), 函数的其余部分也会被 异步求值

–总结: await后面的代码 会被异步执行 !

新版本:

​ promise.resolve() 只会生成一个异步任务

await暂停恢复的方法如下:

​ 1:await 将右边的异步任务推入 消息队列, 等待得到其值

​ 2: 得到右侧的值之后,开始执行await 后面的代码 (以异步形式)—也就是 await 后面的代码也是推入到消息队列中

简单理解:

​ await 右侧的对象: 会被同步执行(比如promise执行器函数), 但是会异步取得其值

​ await 取得右侧的值之后, await之后的代码都是异步执行

----多个异步函数,多个await之间

​ 遵循 同步 --》微任务-》宏任务

​ 消息队列 (异步任务队列 --》先被扫描到的异步任务会先进入这个队列,被先执行)

​ (异步任务执行的时候 可能会产生新的异步任务)

总结: 异步

1:异步:

​ 定时器

​ promise.resolve() 取得期约的值

​ .then() 等期约的方法 里面的处理程序

​ async修饰的函数 await右侧 标志的 取得了其“值” 和await后面的代码

​ ----目前来说:异步就是这些东西

​ —期约的核心:

​ 个人觉得: 将“任务“ 转为异步任务 —》不阻塞JS主线程

​ 监听任务 的返回值-- 正确返回 /错误返回

​ 都有对应的处理程序接应

​ 为期约标记状态,状态对应着 里面代码的执行情况

​ --更富有语义化

​ 构成一套异步操作, 比如:一连串的异步任务队列

​ 等待上一个异步任务完成,才执行下一个异步任务…

​ 总结: 利用Promise 来实现JS异步操作

​ —await

​ 1:一般搭配 promise来使用

​ 2:await 后面的代码 要等到 promise这个异步任务的值落定之后 才会执行

​ —这其实也是一种合理的异步操作

​ 你想: 你要想进行await后面的代码,你是不是应该先接收到某个值 ? 因为这个值是 promise提供的(异步)

​ 所以 await 后面的代码 理应也是 异步执行的

​ 不然 …就阻塞了主线程了!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值