目录
9 请描述event loop(事件循环/事件轮询)的机制,可画图
问答题:请描述event loop(事件循环/事件轮询)的机制,可画图
场景题:promise then和catch的连接(常考且实用)编辑async/await语法编辑
编辑外加async/await的顺序问题(好题,综合深入)
1 单线程、异步、同步
单线程:
-
JS是单线程语言,只能同时做一件事儿
-
浏览器和nodejs已支持JS启动进程,如Web Worker
-
JS和DOM渲染共用同一个进程,因为JS可修改DOM结构
异步:
-
背景:基本js单线程的背景产生,并不是为了异步而异步
-
目的:遇到等待(网络请求,定时任务)不能卡住,使得下面的代码继续执行
-
实现:基于callback函数形式调用
-
原理:event loop 就是异步回调的实现原理
同步:
-
从上到下依次执行代码
-
遇到等待(网络请求,定时任务)会卡住,使得下面的代码不执行
异步与同步:
-
基于js是单线程
-
异步不会阻塞代码执行
-
同步会阻塞代码执行
//异步 callback回调函数
console.log(100)
setTimeout(() => {
console.log(200)
}, 1000)
console.log(300)
// 同步
console.log(100)
alert(200)
console.log(300)
2 callback hell(回调地狱)
3 Promise
解决回调地狱,把多层嵌套改成了管道串联的形式,提高代码可读性
1.主要是用来解决回调地狱的问题 2.所以出现了promise 3.现在主流的方法是使用async 和 await 来进行解决异步问题。 4.async 和 await 本质上是一个语法糖 使函数的返回值包含在promise中返回。
resolve 触发 then,then中的函数接收resolve 的参数 reject 触发 catch,catch中的函数接收reject 的参数
手写Promise加载一张图片
const url = "" // (img url)
function loadImg (src) {
return new Promise(
(resolve, reject) => {
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
reject(new Error('图片加载失败'))
}
img.src = src
}
)
}
loadimg(url).then((img) => {
console.log(img.width)
return img
}).then(img => {
console.log(img.height)
}).catch(ex => console.log(ex)
4 基础知识回顾
-
变量类型与计算
-
typeof判断类型:number string boolean symbol function undefined object,但是对于object不能细分(无法区分是object还是array或者null)
-
=== 和 == 的区别: === 为全等,不存在隐式转换, == 为相等,存在隐式转换(例如'' == 0 为true),通常都用严格全等===,除了极少情况用==(例如obj.a == null)
-
值类型、引用类型区别:值类型存储在栈中,引用类型存储在堆中
-
手写深拷贝
function deepClone(target){
if(typeof target !== 'object' || target === null) return target
let newObj = target instance Array ? [] : {}
for(let key in target){
if(target.hasOwnProperty(key)){
let newObj[key] = deepClone(target[key])
}
}
return newObj
}
5. truly和falsely变量 (a =100,那么!!a = true)
-
原型和原型链
-
如何判断一个变量是不是数组: xxx instanceof Array
-
class本质:原型链 + 子类调用父类构造函数
-
原型与原型链的图示:暂无
6 作用域与闭包
-
this不同场景如何取值:非严格模式下,函数默认指向window,如果使用xxx.fn(),那么this指向调用它的对象;箭头函数没有自己的this,使用的是它父级作用域的this
-
实际开发中闭包的作用:将数据封装起来,并向外暴露调用它的相关api,实现数据的私有化
function createCache(){
const data {}
return {
get(key){
return data[key]
}
set(key,val){
data[key] = val
}
}
}
let data = createCache(){}
data.set('a', 50)
data.get('a') // 50
3.手写bind
Function.prototype.bind1 = function(){
const args = Array.of(...arguments)
const _this = args.shift()
const self = this
return function(){
return self.apply(_this,args)
}
}
5 event loop(事件循环)
event loop(事件循环/事件轮询)机制
js是单线程运行的
异步要基于回调来实现
event loop 就是异步回调的实现原理
js如何执行
从前往后,一行一行执行
如果某一行执行报错,则停止下面代码的执行
先把同步代码执行完,再执行异步
event loop
执行图解:
-
Call Stack:调用栈(同步任务队列)
-
Web APIs:浏览器运行环境对自己Api的一些定义,如:定时器,Dom事件...(宏任务队列)
-
Event Loop:事件循环/轮询机制
-
Callback Queue:回调函数队列
执行总结:
-
同步代码,一行一行放在Call Stack执行
-
遇到异步,会先“记录”下,等待时机(定时、网络请求等)
-
时机到了,就移动到Callback Queue
-
如Call Stack为空(即同步代码执行完),先尝试DOM渲染后,Event Loop再开始工作
-
轮询查找Callback Queue,如有则移动到Call Stack执行
-
然后继续轮询查找(永动机一样)
6 promise进阶
promise 有三种状态,分别是:pending(初始化状态)、resolved(成功状态)、rejected(失败状态)
pending是当用户创建了promise对象时的状态,即new Promise( resolved , rejected ){ }
此时后面两个都未被调用,所以为初始化pending。该状态下,不能触发then和catch
resolved是当用户触发后的成功状态,该状态下,只能触发then的回调函数,不能触发catch
rejected是当用户触发后的失败状态,该状态下,只能触发catch的回调函数,不能触发then
三种状态:
pending(等待结果)resolved(已成功) rejected(已失败)
pending -> resolved 或 pending -> rejected
变化不可逆
状态的表现和变化:
pending状态,不会触发 then 和 catch
resolved状态,会触发后续的 then 回调函数
rejected状态,会触发后续的 catch 回调函数
then 和 catch 对状态的影响
-
then 正常返回resolve状态的promise,如果有报错,返回的是reject
-
catch正常返回resolve状态的promise,如果有保持,返回的是reject
3 resolve会触发then的回调, reject会触发catch的回调
7 async/await
- 解决异步回调callback hell(回调地狱)
- Promise then catch 链式调用,但也是基于回调函数
- async/await(同步语法编写异步代码)
- 是同步语法,彻底消灭回调函数
匿名函数前面经常会加 ! ,用来区分和前面的代码
await后面,可以是Promise,也可以是async函数;
async:
-
返回的是一个 Promise 对象
-
若返回值不是 Promise 对象,则相当于返回 Promise.resolve(xxx)
await:
-
只能在 async 函数中使用
-
await 相当于 Promise 的 then
-
所以 await 后面是一个 Promise 对象,若不是,则相当于 Promise.resolve(xxx)
-
且 await 下面的代码,相当于放在一个callback回调中,即异步,且属于微任务
-
若 await 后面的是一个 rejected 状态的 Promise 对象,则 await 下面的代码不会执行
async/await 和 Promise 关系?
◆执行async函数,返回的是Promise对象
◆await 相当于Promise的then
◆try..catch 可捕获异常, 代替了Promise的catch
◆async/await 只是一个语法糖,但这颗糖真香!
8 宏任务/微任务
ES6 规范中,microtask 称为 jobs,macrotask 称为 task
宏任务是由宿主发起的,而微任务由JavaScript自身发起。
macro Task vs micro Task
js 是单线程,且和 DOM 渲染共用同一个线程,因为js执行过程中,需要留一些时间给 DOM 完成渲染
call stack空闲的时候, 1 event loop 2 进行dom渲染 3 将call Queue队列里的回调函数添加到call stack里执行 1 event loop 回到第一步
宏任务dom渲染后触发 浏览器规定 放在callbackqueue
微任务dom渲染前触发 Es6语法规定 放在 micro task queue
所以微任务触发早于宏任务
微任务在DOM渲染之前触发,宏任务在DOM渲染之后触发,参考代码
所以真实的执行Event loop
1、call stack 2、微任务(存于微任务队列) 3、DOM渲染 4、触发Event loop(宏任务在这)
总结
9 请描述event loop(事件循环/事件轮询)的机制,可画图
event loop(事件循环/事件轮询) 1. JS 是单线程运行的 2. 异步要基于回调来实现 3. event loop 就是异步回调的实现机制
event loop执行图解:
-
Call Stack:调用栈(同步任务队列)
-
Web APIs:浏览器运行环境对自己Api的一些定义,如:定时器,Dom事件...(宏任务队列)
-
Event Loop:事件循环/轮询机制
-
Callback Queue:回调函数队列
同步代码,一行一行放在CallStack执行
遇到异步,会先“记录”下,等待时机(定时、网络请求等)
时机到了,就移动到Callback Queue
如Call Stack为空(即同步代码执行完)Event Loop开始工作
轮询查找Callback Queue,如有则移动到Call Stack执行
然后继续轮询查找(永动机一样)
JS 如何执行?
-
从前到后,一行一行执行
-
如果某一行执行报错,则停止下面代码执行
-
先把同步代码执行完,在执行异步
总结 event loop 执行过程
-
同步代码,一行一行放在 Call Stack 执行
-
遇到异步,会先“记录”下,等待时机(定时,网络请求等)
-
时机到了,就会移动到 Callback Queue
-
如果 Call Stack 为空(即异步代码执行完)Event loop 开始工作
-
轮训查找 Callback Queue,如有则移动到 Call Stack 执行
-
然后继续轮询查找(永动机一样)
DOM 事件和 event loop 的关系
-
JS 是单线程的
-
异步(setTimeout,Ajax 等)使用回调,基宇 event loop
-
DOM 事件也使用回调,基宇 event loop
DOM渲染和Event Loop:
-
Call Stack为空时,都会先尝试DOM渲染,再触发Event Loop
问答题:
请描述event loop(事件循环/事件轮询)的机制,可画图
![](https://img-blog.csdnimg.cn/078e454f517b44ffa07db4b9e688e914.png)
什么是宏任务、微任务,两者有什么区别
![](https://img-blog.csdnimg.cn/753edf00b03646509fbe3414f24e88e3.png)
Promise有哪三种状态,如何变化
场景题:
promise then和catch的连接(常考且实用)![](https://img-blog.csdnimg.cn/6fb513074af84573802b7667fa54c341.png)
async/await语法![](https://img-blog.csdnimg.cn/55d127f0b9dd41b88c0dad15cf17f9c9.png)
a = 100 b= 一个 promise
promise和setTimeout的顺序(常考)
![](https://img-blog.csdnimg.cn/0440d676482249edb0657758361055e5.png)
外加async/await的顺序问题(好题,综合深入)
手写priomise
const PENDING ='pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise{
state = PENDING
value = null
reason = null
onFulfilledCallbacks = [] // 记录成功的回调
onRejectedCallbacks = [] // 记录失败的回调
constructor(executor){
try{
executor(this.resolveHandler,this.rejectHandler)
}catch(err){
this.rejectHandler(err)
}
}
resolveHandler = (value) => {
if(this.state === PENDING){
this.state = FULFILLED
this.value = value
while(true){
this.onFulfilledCallbacks.shift()(this.value)
}
}
}
rejectHandler = (reason) => {
if(this.state === PENDING){
this.state = REJECTED
this.reason = reason
while(true){
this.onRejectedCallbacks.shift()(this.value)
}
}
}
then(onFulfilled, onRejected){
const realOnfulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
const realOnRejected = typeof onRejected === 'function ? onRejected : reason => reason
const promise = new MyPromise((resolve, reject)=>{
if(this.state === FULFILLED){
try{
const newValue = realOnfulfilled(this.value)
resolvePromise(newValue, resolve, reject)
}catch(err){
reject(err)
}
}else if(this.state === REJECTED){
try{
const newReason = realOnRejected(this.value)
}catch(err){
reject(err)
}
}else if(this.state === PENDING){
this.onFulfilledCallbacks.push(realOnFulfilled)
this.onRejectedCallbacks.push(realOnRejected)
}
})
return promise
}
}
function resolvePromise(newValue, resolve, reject ){
// 判断产生的Promise是否为Promise本身
if(newValue instanceOf MyPromise){
newValue.then(resolve, reject)
}else{
resolve(newValue)
}
}