1.event loop
1. 请描述event loop(事件循环/事件轮询)的机制
1.JS是单线程运行的.(如何执行?)
a. 从前到后一行一行的执行
b. 一旦有一行执行报错,则阻断后面代码的执行
c. 先把同步代码执行完,再执行异步
2.异步要基于回调来实现
3.event loop 就是异步回调的实现原理
a. 同步代码,一行一行放在Call Satck执行
b. 遇到异步,会先"进行记录",等待时机(定时,网络请求)
c. 时机(时间到了),移动到callback queue(回调队列)中
d. 如果CallSatck为空(同步代码执行完) Event Loop 开始工作
e. 开始轮询查找Callback Queue,找到之后移动到Call Stack 执行
f. 然后继续轮询查找,直至为空
2. DOM事件和event loop事件循环
<button id='btn'>提交</button>
<script>
console.log('hi');
$('#btn').click(() => {
console.log('button clicked');
})
console.log('Bye');
// 它会先执行一遍 hi---$('#btn').click()---Bye----用户点击的时候触发回调
</script>
1.JS是单线程的
2.异步(setTimeout, ajax等) 使用回调,基于event loop
3.DOM 事件也使用回调,基于event loop
dom事件虽然是基于event loop实现,但它不是异步
Promise 进阶
1.Promise有哪三种状态? 是如何变化的?
1.三种状态
pending(初始化)
fulfilled(成功)
rejected(失败)
2.状态的表现和变化
变化是不可逆的, promise状态一旦被改变,就不允许被改变了
-----pending---> resolved 或 pending ---> rejected
const p1 = new Promise((res,rej) => {})
console.log('p1', p1); // pending
const p2 = new Promise((res,rej) => {
setTimeout(() => {
res();
})
})
console.log('p2', p2); // 开始打印时候为pending
setTimeout(() => console.log('p2-setTimeout', p2)); // res
const p3 = new Promise((res,rej) => {
setTimeout(() => {
rej();
})
})
console.log('p1', p3); // pending
setTimeout(() => console.log('p3-setTimeout', p3)); // rej
表现
pending 不会触发then和catch
resolved 会触发后续的then回调函数
reject 会触发后续的catch回调函数
const w1 = Promise.resolve(10); // resolved
w1.then(res => console.log('res', res)) // 10
.catch(err => console.log('err', err)) // 不会触发
const w2 = Promise.resolve(10); // rejected
w2.then(res => console.log('res', res)) // 不会触发
.catch(err => console.log('err', err)) // 10
3.then和catch对状态的影响
then 正常返回resolved,里面有报错则返回rejected
const w1 = Promise.resolve().then(() => 10)
console.log('w1', w1); // resolved 触发 then回调
w1.then(() => {
console.log('123); // 触发执行输出123
});
const w2 = Promise.resolve().then(() => {
throw new Error('then error')
})
w2.then(() => {
console.log('456') // 不会被触发执行
}).catch(err => {
console.error('error', err) // then error
})
console.log('w2', w2); // rejected 触发 catch 回调
catch 正常返回resolved,里面有报错则返回rejected
const p4 = Promise.reject('then error').catch(err => console.log('err', err))
console.log('p4', p4); // resolved? 为什么? 因为只要正常返回那就是resolved 触发 then
p4.then(() => {
console.log(100); // 100
})
const p5 = Promise.reject('then error').catch(err => {
throw new Error('catch error')
})
console.log('p5', p5); // rejected 触发 catch
p5.then(() => {
console.log(200); // 不会执行
}).catch(() => {
console.log('error'); // error
}) // resloved
2.Promise .then和.catch的连接
场景一
Promise.resolve().then(() => { // Promise.resolve() 返回resolve的promise
console.log(1); // 1
}).catch(() => { // 所以catch 不会被执行
console.log(2);
}).then(() => {
console.log(3); // 3
}) // resolved
场景二
Promise.resolve().then(() => { // 返回 rejected的promise
console.log(1); // 1
throw new Error('error');
}).catch(() => { // 没有报错 返回 resolved 的 promise
console.log(2); // 2
}).then(() => {
console.log(3); // 3
}) // 因为最后一个也没有报错会返回resolved
场景三
Promise.resolve().then(() => { // rejected 的promise 触发 catch
console.log(1); // 1
throw new Error('error');
}).catch(() => { // 返回 resolved 的 promise 后面是不会被执行的 且触发 then
console.log(2); // 2
}).catch(() => { // 不会再被执行
console.log(3);
})
3. Promise和setTimeout的顺序
场景一
console.log(100) // 1
setTimeout(() => {
console.log(200); // 4 宏任务
})
Promise.resolve().then(() => {
console.log(300); // 3 微任务
})
console.log(400); // 2
async / await
1.场景题 async/await语法
(1)基本应用
1.最原始的办法 callback hell (回调地狱) 缺点 层层嵌套
2.Promise then catch 链式调用,也是基于回调函数
3.async / await 同步语法, 同步代码编写异步语法,彻底解决回调函数
const src1 = "地址";
const src2 = "地址";
function loadImg (src) {
const p = new Promise((res,rej) => {
const img = document.createElement('img')
img.onload = () => {
res(img) // resolved
}
img.onerror = () => {
const err = new Error(`图片加载失败${src}`)
rej(err) // rejected
}
img.src = src
})
return p
}
// !的意识是防止 在定义时候src1 src2 中间没有用分号分割 会把src1/2当成一个方法来执行就会报错
!(async function () {
// 同步写法执行代码
const img1 = await loadImg(src1)
console.log(img1.height, img1.width);
const img2 = await loadImg(src2)
console.log(img2.height, img2.width);
})()
1.async/await 是避免使用异步回调的一种方法 不过现在也没人用异步回调解决
2.但和Promise并不相互排斥
3.反而俩者相辅相成
(2).async/await和Promise的关系
1.执行async函数,返回的是一个Promise对象
2.await 相当于 Promise 的 then
3.try...catch 可捕获异常,代替了Promise 的 catch
// 示例
async function fn1 () {
return 100; // === return Promise.resolve(100)
}
const res = fn1(); // 执行async函数,返回的是一个Promise对象 resolved
res.then(v => console.log('data', v)) // 200
// await 返回 promise 对象 相当于 then
!(async function () {
const p1 = Promise.resolve(300)
const res = await p1 // 1. await 相当于 Promise then
const res1 = await 400 // 2. await Promise.resolve(400)
const res2 = await fn1() // 3. await Promise.resolve(100)
console.log('res', res); // 300
console.log('res1', res1); // 400
console.log('res2', res2); // 100
})()
// try catch 捕获
!(async function () {
const p1 = Promise.reject('err1'); // rejected 状态
try {
const res = await p1;
console.log(res, '---res--');
} catch (error) {
console.log(error); // try...catch 相当于 promise catch
}
})()
// 错误示例
!(async function () {
const p1 = Promise.reject('error111') // rejected 状态
const res = await p1; // 因为await 相当于 then 但是它的状态是rejected 肯定不会执行到await
console.log(res, '---res--');
})()
(3)场景题
// 示例1
(async function () {
console.log('start'); // start
const a = await 120
console.log('a', a); // 120
const b = await Promise.resolve(120)
console.log('b', b); // 120
const c = await Promise.reject(300) // 报错, 因为await 相当于 promise .then
console.log('c', c);
console.log('end');
})() // 执行完之后,打印哪些内容? start 120 120
// 示例2
async function fn() {
return 100
}
(async function () {
const a = fn() // promise对象
const b = await fn() // 100
})()
2. async / await 的顺序问题
(1)场景题
async function async1 () {
console.log('async1 start'); // 2
await async2()
// await 后面的会做为 异步微任务
console.log('async1 end'); // 6
}
async function async2 () {
console.log('async2'); // 3
}
console.log('script start'); // 1
setTimeout(() => {
console.log('setTimeout'); // 8
}, 0)
async1()
// new 初始化 promise 时, 传入的函数会立刻被执行
new Promise (function(resolve) {
console.log('promise1'); // 4
resolve() // 执行resolve之后 初始化的整个状态就是resloved
}).then(function(){ // 微任务
console.log('promise2'); // 7
})
console.log('start end'); // 5 // 此段代码 依次打印输出什么?
微任务 / 宏任务
1.什么是宏任务(macroTask)和微任务(microTask)、两者有什么区别?
1.宏任务: DOM 渲染后触发 例: setTimeout
setTimeout, setInterval, Ajax, DOM 事件
2.微任务: DOM 渲染前触发,例: Promise (执行时机比宏任务早)
Promise async/await
3.event loop 和 DOM 渲染
每次 Call Satck 清空(即每次轮询结束), 同步任务执行完
都是DOM重新渲染的机会,DOM结构如有改变则重新渲染
然后再去触发下一次Event loop
4. 为什么先执行微任务后执行宏任务?
1. 同步代码,一行一行放在Call Satck执行
2. 遇到异步,会先"进行记录",等待时机(定时,网络请求)
3. 时机到了,移动到callback queue(回调队列)中
4. 如果CallSatck为空(同步代码执行完) Event Loop 开始工作
5. callback queue 回调队列中还会分为微任务/宏任务 ,先执行微任务,然后进行尝 试DOM渲染
6. 触发event loop 轮询查找Callback Queue,找到之后移动到Call Stack 执行
7. 然后继续轮询查找,直至为空
5.微任务和宏任务区别?
1. 微任务是es6 语法规定的
2. 宏任务是由浏览器规定的
3. 微任务执行时机比宏任务早
4. 宏任务: DOM 渲染后触发
5. 微任务: DOM 渲染前触发
手写Promise-构造函数
1.初始化 & 异步调用
2.then catch 链式调用
3.API .resolve .reject .all .race
(1)构造函数实现
/**
* @description MyPromise
* @author Amorous
*/
class MyPromise {
state = 'pending' // 状态, 'pending' 'fulfilled' 'rejected'
value = undefined // 成功后的值
reason = undefined // 失败后的原因
resolveCallbacks = [] // pending 状态下 存储成功的回调
rejectedCallbacks = [] // pending 状态下 存储失败的回调
constructor(fn) {
const resolveHandler = (value) => {
if(this.state === 'pending') {
this.state === 'fulfilled'
this.value === value
this.resolveCallbacks.forEach(fn => fn(this.value))
}
}
const rejectedHandler = (reason) => {
if(this.state === 'pending') {
this.state === 'rejected'
this.reason === reason
this.resolveCallbacks.forEach(fn => fn(this.reason))
}
}
try {
fn(resolveHandler, rejectedHandler)
} catch (err) {
rejectedHandler(err)
}
}
then()
catch()
resolve()
reject()
all()
race()
}
(2).then 实现
then(fn1, fn2) {
fn1 = typeof fn1 === 'function' ? fn1 : (v) => v
fn2 = typeof fn2 === 'function' ? fn2 : (err) => err
const { state } = this;
if (state === 'pending') {
const p1 = new MyPromise((resolve, reject) => {
this.resolveCallbacks.push(() => {
try {
const newValue = fn1(this.value)
resolve(newValue)
} catch (error) {
reject(error)
}
})
this.rejectedCallbacks.push(() => {
try {
const newReason = fn2(this.reason)
reject(newReason)
} catch (error) {
reject(error)
}
})
})
return p1
}
if (state === 'fulfilled') {
const p1 = new MyPromise((resolve, reject) => {
try {
const newData = fn1(this.value);
resolve(newData)
} catch (error) {
reject(err)
}
})
return p1
}
if (state === 'rejected') {
const p1 = new MyPromise((resolve, reject) => {
try {
const newReason = fn2(this.reason);
reject(newReason)
} catch (error) {
reject(err)
}
})
return p1
}
}
}
(3).catch 实现
// 就是then的一个语法糖,简单模式
catch (fn) {
return this.then(null, fn)
}
(4)resolve 实现
MyPromise.resolve = function(value) {
return new MyPromise((resolve, reject) => resolve(value))
}
(5)reject 实现
// reject
MyPromise.reject = function(reason) {
return new MyPromise((resolve, reject) => reject(reason))
}
(6)all 实现
MyPromise.all = function (promiseList = []) {
const p1 = new MyPromise((resolve, reject) => {
const result = [] // 存储 promiseList 所有的结果
const length = promiseList.length
let resolvedCount = 0;
promiseList.forEach(p => {
p.then(data => {
result.push(data)
// resolvedCount 必须在then里面做++ .then执行了才++
resolvedCount++
if (resolvedCount === length) {
// 已经遍历到了最后一个 promise
resolve(result)
}
}).catch(err => {
reject(err)
})
})
})
return p1
}
(7)race 实现
MyPromise.race = function (promiseList = []) {
let resolved = false // 标记
const p1 = new Promise((resolve, reject) => {
promiseList.forEach(p => {
p.then(data => {
if (!resolved) {
resolve(data)
resolved = true
}
}).catch(err => {
reject(err)
})
})
})
return p1
}
原文链接:https://juejin.cn/post/7217057805782450233 作者:Amorous