promise学习
-
游览器是多线程,但是它只分配一个"JS 引擎线程"用来渲染和解析 js 代码,所以 JS 是单线程的!!
- JS 中大部分代码都是"同步编程",例如:循环...
- 千万不要写死循环,一旦死循环则 JS 引擎会一直被占用,其它事情都做不了
- 遇到程序抛出一场,后面的代码不会再执行
throw new Error;//手动抛出异常- 我们基于 try{}catch{}进行异常捕获,这样不会影响后续代码执行
- JS 中大部分代码都是"同步编程",例如:循环...
-
JS 中也存在异步编程依托于浏览器多线程,在基于 EventLoop 事件循环机制处理的
- 异步宏任务 macrotask
- 定时器 setTimeout/setInterval
- 事件绑定/队列
- ajax/fetch
- ...
- 异步微任务 microtask
- requestAnimationFrame
- promise.then./catch/finally
- async await
- queueMicrotask 基于这个方法可以黄建一个异步微任务
- IntersectionObserver
- ...
- 异步宏任务 macrotask
-
游览器打开一个页面,除了开辟堆栈内存,还会默认创建两个队列
- WebAPI 队列:检测异步任务是否可以执行
- EventQueue 队列:存储所有可执行的异步任务,在这个队列中排队等待执行 - 异步微任务 - 异步宏任务
-
定时器到时间后也不一定能执行(设定的时间是其最快的执行时间):如果此时主线程被占用,则必须等主线程空闲下来,排在 EventQueue 中的定时器才可以执行
-
基于 JS 和定时器实现动画效果会存在问题
- 出现卡顿的情况:到时见该走了,但是主线程被占用,它不走了
- 我们设定的时间很难和"屏幕刷新率"保持一致
-
但是可以基于 window.requestAnimationFrame 实现动画
- 不需要设置时间,默认是按照电脑的"屏幕刷新率对应的时间"进行运动的
- 也会出现"因主线程被占用,它无法立即执行"导致卡顿,但是比定时器好,因为他是异步微任务,优先于异步宏任务执行!!
-
-
promise:ES6 新增的内置类(构造函数),用来规划异步编程代码,解决回调地狱问题
-
7. let p1 = new Promise([executor])
- [executor]必须是一个函数,而且 new Promise 的时候必须把其执行"同步"
- p1 是其创建出来的实例
- 私有属性有
- [[PromiseState]]:"pending"、"fulfilled"、"rejected"实例状态
- [[PromiseResult]]:undefined 实例的值[成功的结果或失败的原因]
- 公共方法:Promise.prototype
- then
- catch
- finally
- ...
- p1.then([onfulfilled],[onrejected])
- [onfulfilled]/[onrejected]都是函数
- 实例状态是成功 fulfilled 的时候,会把[onfulfilled]执行,并且把实例的值作为成功的结构传递给他
- 实例的状态是失败 rejected 的时候,会把[onrejected]执行,把实例的值作为失败的原因传递给他
- 私有属性有
- 如何修改实例的状态和值
- 基于这种方式创建实例
let p = new Promise(()=>{...})- resolve/reject 也是函数
resolve('ok')->把实例 p 的状态修改为 fulfilled,值(成功结果)是'ok'
reject('no')->把实 p 的状态修改为 rejected,值(失败原因)是'no' - 如果 executor 函数执行报错,则把实例额状态修改为 reject,值是报错原因[不会抛异常]
- 一旦状态被修改为 fulfilled 或 rejected,后期就不会在更改状态值了
- resolve/reject 也是函数
- 每一次执行 then 方法,都会返回一个"全新的 prominse 实例"
let p2 = p1.then(onfulfilled,onrejected);不论是 onfulfilled 还是 onrejected 执行(由 p1 状态决定),方法的执行决定了 p2 的状态和值
- 首先看方法执行是否报错,如果报错了,则 p2 是失败态(rejected),值是报错原因
- 如果执行不报错,再看方法的放回值
- 如果返回的是"新的 promise 实例->@np,则@np 的状态值直接决定了 p2 的状态和值"
- 如果返回的不是新实例,则 p2 撞他是成功(fulfilled),值是函数返回值
- 执行 Promise.resolve/reject/all/any/race...等静态私有方法,也会创建新的 promise 实例
- Promise.resolve(10)创建一个状态是成功 fulfilled,值是 10 的实例
- Promise.reject(0)创建一个状态是失败 rejected,值是 0 的实例
- 基于这种方式创建实例
p1.then( value =>{
console.log('成功',value)
},reason => {
console.log('失败',reason)
})
- then 链的穿透/顺延机制
- .then(onfulfilled,onrejected),两个方法可以传可以不传,如果不传则顺延至下一个 then 中,相同状态执行的方法中去处理!
- 原理:我们不设置对应的方法,promise 内部会默认加一个方法,可以让其实现状态顺延/穿透
p.catch(onrejected)等价与p.then(null,onrejected)
- 原理:我们不设置对应的方法,promise 内部会默认加一个方法,可以让其实现状态顺延/穿透
- 真实项目中:then 中一般只传递 onfulfilled[成功干什么],最后一个 catch;这样不论中间哪个环节创建了失败的实例,都会穿透至最后一个 catch;catch 不加,出现失败案例,控制台报"红",但是不影响其他代码执行!!
- 关于 Promise.all/any/race 第三个方法的研究
let p = Promise.all([promises]);
- Promise 是包含零到多个 primise 实例的集合,一般是数组!如果集合中的某一项不是 promise 实例,则默认变为状态为成功,值是本身的 promise 实例!!
- all:集合中的"每个实例都成功",最后结果 p 才成功,值是按照结合顺序,一次存储每个实例成功结果的数组;其中只要有一个实例失败,则 p 就失败的,值是本次失败的原因,后面的操作不再处理!!
- any:只有一个成功,最后 p 就是成功的,值是本次成功的结果;都失败,最后 p 才是失败!{兼容性不好}
- race:集合中谁最先指导结果,则以谁的为主!
-
AJAX 的串行和并行:真实项目中发送 ajax 请求都是"采用异步编程"
- 并行:多个请求通水发送即可,谁先回来先处理谁,主要用于多个请求间没有依赖[偶尔我们需要检测,多个请求都成功,整体再去做啥事 => Promise.all]
- 串行:多个请求之间存在依赖,上一个请求成功,我们才能发送下一个请求(往往是下一个请求需要用到上一个请求的结果,才需要这样处理)!
-
. then 方法,用来处理单个 promise 方法返回的成功和失败后的处理,. then()括号中会调用两个函数,第一函数用于处理成功后的事件,第二个函数用来调用失败后的函数 quadratic > 如果只想调用失败后的处理方法,可以. then(null,error),可以在第一个函数位置传个 null,它等同于调用 catch 方法:. catch(error)
jscript;
// 创建一个任务对象,该任务立即进入 pending 状态
const pro = new Promise((resolve, reject) => {
// 任务的具体执行流程,该函数会立即被执行
// 调用 resolve(data),可将任务变为 fulfilled 状态, data 为需要传递的相关数据
// 调用 reject(reason),可将任务变为 rejected 状态,reason 为需要传递的失败原因
});
pro.then(
data => {
// onFulfilled 函数,当任务完成后,会自动运行该函数,data 为任务完成的相关数据
},
reason => {
// onRejected 函数,当任务失败后,会自动运行该函数,reason 为任务失败的相关原因
}
);
- then 方法必定会返回一个新的 Promise
- 可理解为
后续处理也是一个任务
- 新任务的状态取决于后续处理:
-
若没有相关的后续处理,新任务的状态和前任务一致,数据为前任务的数据
-
若有后续处理但还未执行,新任务挂起。
-
若后续处理执行了,则根据后续处理的情况确定新任务的状态
- 后续处理执行无错,新任务的状态为完成,数据为后续处理的返回值
- 后续处理执行有错,新任务的状态为失败,数据为异常对象
- 后续执行后返回的是一个任务对象,新任务的状态和数据与该任务对象一
- Promise 的静态方法
| 方法名 | 含义 |
|---|---|
| Promise. resolve(data) | 直接返回一个完成状态的任务 |
| Promise. reject(reason) | 直接返回一个拒绝状态的任务 |
| Promise. all(任务数组) | 返回一个任务 任务数组全部成功则成功 任何一个失败则失败 |
| Promise. any(任务数组) | 返回一个任务 任务数组任一成功则成功 任务全部失败则失败 |
| Promise. allSettled(任务数组) | 返回一个任务 任务数组全部已决则成功 该任务不会失败 |
| Promise. race(任务数组) | 返回一个任务 任务数组任一已决则已决,状态和其一致 |
- 使用方法 Promise. all([括号中需要填写执行数组])
- 将多维数组展开使用. flat()方法
let arry = [
[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
];
console.log(arry.flat()); //[1,2,3,1,2,3,1,2,3];
- 消除回调
有了 Promise,异步任务就有了一种统一的处理方式
有了统一的处理方式,ES 官方就可以对其进一步优化
ES7 推出了两个关键字async和await,用于更加优雅的表达 Promise
- async
async 关键字用于修饰函数,被它修饰的函数,一定返回 Promise
async function method1() {
return 1; // 该函数的返回值是Promise完成后的数据
}
method1(); // Promise { 1 }
async function method2() {
return Promise.resolve(1); // 若返回的是Promise,则method得到的Promise状态和其一致
}
method2(); // Promise { 1 }
async function method3() {
throw new Error(1); // 若执行过程报错,则任务是rejected
}
method3(); // Promise { <rejected> Error(1) }
- await
await关键字表示等待某个 Promise 完成,它必须用于async函数中
async function method() {
const n = await Promise.resolve(1);
console.log(n); // 1
}
// 上面的函数等同于
function method() {
return new Promise((resolve, reject) => {
Promise.resolve(1).then(n => {
console.log(n);
resolve(1);
});
});
}
await也可以等待其他数据
async function method() {
const n = await 1; // 等同于 await Promise. resolve(1)
}
如果需要针对失败的任务进行处理,可以使用try-catch语法
async function method() {
try {
const n = await Promise.reject(123); // 这句代码将抛出异常
console.log("成功", n);
} catch (err) {
console.log("失败", err);
}
}
method(); // 输出: 失败 123
let p1 = new Promise((res, rej) => {
// res rej 是两个函数体,Promise 给的
// rej(777)把 p1 变成了 rejected(失败态)
// res(888)把 p1 变成了 fulfilled/resolved(成功态)
//res 不执行 rej 也不执行 pending(等待态)
// p1 一旦变成了 rejected 或者 fulfilled 那么就不会在去改变
//promise 的实例肯定能由三种状态:原始状态是 pending,成功态 resolved/fulfilled 失败态 rejected
//实例状态:只能由 pending 变成 fulfilled 或者 rejected;不能有 fulfilled 变成 rejected 或者 fulfilled
//该函数报错的时候实例状态也会变成 rejected
});