游览器中js运行机制及promise学习内容

92 阅读9分钟

promise学习

  1. 游览器是多线程,但是它只分配一个"JS 引擎线程"用来渲染和解析 js 代码,所以 JS 是单线程的!!

    • JS 中大部分代码都是"同步编程",例如:循环...
      • 千万不要写死循环,一旦死循环则 JS 引擎会一直被占用,其它事情都做不了
      • 遇到程序抛出一场,后面的代码不会再执行throw new Error;//手动抛出异常
        • 我们基于 try{}catch{}进行异常捕获,这样不会影响后续代码执行
  2. JS 中也存在异步编程依托于浏览器多线程,在基于 EventLoop 事件循环机制处理的

    • 异步宏任务 macrotask
      • 定时器 setTimeout/setInterval
      • 事件绑定/队列
      • ajax/fetch
      • ...
    • 异步微任务 microtask
      • requestAnimationFrame
      • promise.then./catch/finally
      • async await
      • queueMicrotask 基于这个方法可以黄建一个异步微任务
      • IntersectionObserver
      • ...
  3. 游览器打开一个页面,除了开辟堆栈内存,还会默认创建两个队列

    • WebAPI 队列:检测异步任务是否可以执行
    • EventQueue 队列:存储所有可执行的异步任务,在这个队列中排队等待执行 - 异步微任务 - 异步宏任务 事件循环机制
  4. 定时器到时间后也不一定能执行(设定的时间是其最快的执行时间):如果此时主线程被占用,则必须等主线程空闲下来,排在 EventQueue 中的定时器才可以执行

    • 基于 JS 和定时器实现动画效果会存在问题

      • 出现卡顿的情况:到时见该走了,但是主线程被占用,它不走了
      • 我们设定的时间很难和"屏幕刷新率"保持一致
    • 但是可以基于 window.requestAnimationFrame 实现动画

      • 不需要设置时间,默认是按照电脑的"屏幕刷新率对应的时间"进行运动的
      • 也会出现"因主线程被占用,它无法立即执行"导致卡顿,但是比定时器好,因为他是异步微任务,优先于异步宏任务执行!!
  5. promise:ES6 新增的内置类(构造函数),用来规划异步编程代码,解决回调地狱问题


20220412192423 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]执行,把实例的值作为失败的原因传递给他
  • 如何修改实例的状态和值
    1. 基于这种方式创建实例 let p = new Promise(()=>{...})
      • resolve/reject 也是函数

        resolve('ok')->把实例 p 的状态修改为 fulfilled,值(成功结果)是'ok'
        reject('no')->把实 p 的状态修改为 rejected,值(失败原因)是'no'

      • 如果 executor 函数执行报错,则把实例额状态修改为 reject,值是报错原因[不会抛异常]
      • 一旦状态被修改为 fulfilled 或 rejected,后期就不会在更改状态值了
    2. 每一次执行 then 方法,都会返回一个"全新的 prominse 实例"
      let p2 = p1.then(onfulfilled,onrejected);

      不论是 onfulfilled 还是 onrejected 执行(由 p1 状态决定),方法的执行决定了 p2 的状态和值

      • 首先看方法执行是否报错,如果报错了,则 p2 是失败态(rejected),值是报错原因
      • 如果执行不报错,再看方法的放回值
        • 如果返回的是"新的 promise 实例->@np,则@np 的状态值直接决定了 p2 的状态和值"
        • 如果返回的不是新实例,则 p2 撞他是成功(fulfilled),值是函数返回值
    3. 执行 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)
  })
  1. then 链的穿透/顺延机制
  • .then(onfulfilled,onrejected),两个方法可以传可以不传,如果不传则顺延至下一个 then 中,相同状态执行的方法中去处理!
    • 原理:我们不设置对应的方法,promise 内部会默认加一个方法,可以让其实现状态顺延/穿透 p.catch(onrejected)等价与p.then(null,onrejected)
  1. 真实项目中:then 中一般只传递 onfulfilled[成功干什么],最后一个 catch;这样不论中间哪个环节创建了失败的实例,都会穿透至最后一个 catch;catch 不加,出现失败案例,控制台报"红",但是不影响其他代码执行!!
  2. 关于 Promise.all/any/race 第三个方法的研究
    let p = Promise.all([promises]);
  • Promise 是包含零到多个 primise 实例的集合,一般是数组!如果集合中的某一项不是 promise 实例,则默认变为状态为成功,值是本身的 promise 实例!!
  • all:集合中的"每个实例都成功",最后结果 p 才成功,值是按照结合顺序,一次存储每个实例成功结果的数组;其中只要有一个实例失败,则 p 就失败的,值是本次失败的原因,后面的操作不再处理!!
  • any:只有一个成功,最后 p 就是成功的,值是本次成功的结果;都失败,最后 p 才是失败!{兼容性不好}
  • race:集合中谁最先指导结果,则以谁的为主!
  1. AJAX 的串行和并行:真实项目中发送 ajax 请求都是"采用异步编程"

    • 并行:多个请求通水发送即可,谁先回来先处理谁,主要用于多个请求间没有依赖[偶尔我们需要检测,多个请求都成功,整体再去做啥事 => Promise.all]
    • 串行:多个请求之间存在依赖,上一个请求成功,我们才能发送下一个请求(往往是下一个请求需要用到上一个请求的结果,才需要这样处理)!
  2. . 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 为任务失败的相关原因
	}
);
  1. then 方法必定会返回一个新的 Promise
  • 可理解为后续处理也是一个任务
  1. 新任务的状态取决于后续处理:
  • 若没有相关的后续处理,新任务的状态和前任务一致,数据为前任务的数据

  • 若有后续处理但还未执行,新任务挂起。

  • 若后续处理执行了,则根据后续处理的情况确定新任务的状态

    • 后续处理执行无错,新任务的状态为完成,数据为后续处理的返回值
    • 后续处理执行有错,新任务的状态为失败,数据为异常对象
    • 后续执行后返回的是一个任务对象,新任务的状态和数据与该任务对象一
  1. Promise 的静态方法
方法名含义
Promise. resolve(data)直接返回一个完成状态的任务
Promise. reject(reason)直接返回一个拒绝状态的任务
Promise. all(任务数组)返回一个任务
任务数组全部成功则成功
任何一个失败则失败
Promise. any(任务数组)返回一个任务
任务数组任一成功则成功
任务全部失败则失败
Promise. allSettled(任务数组)返回一个任务
任务数组全部已决则成功
该任务不会失败
Promise. race(任务数组)返回一个任务
任务数组任一已决则已决,状态和其一致
  • 使用方法 Promise. all([括号中需要填写执行数组])
  1. 将多维数组展开使用. 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];
  1. 消除回调

有了 Promise,异步任务就有了一种统一的处理方式

有了统一的处理方式,ES 官方就可以对其进一步优化

ES7 推出了两个关键字asyncawait,用于更加优雅的表达 Promise

  1. 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) }
  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
});