EventLoop

浏览器是多线程的

  • GUI渲染线程:渲染和解析页面
  • JS引擎线程:渲染和解析JS 浏览器分配一个线程去解析JS,所以JS是单线程的
  • 定时器监听线程
  • 事件监听线程
  • HTTP网络请求线程 同源下,浏览器最多同时分配5-7个HTTP线程(HTTP并发数)
  • …web worker 是什么

JS是单线程运行的,所以其中大部分代码都是“同步”的(例如:循环

  • 所以在JS中千万不要写死循环、死递归等操作,这些操作会一直占用JS线程引擎,导致后续其他的程序都无法执行
  • 但是JS中也有部分异步操作的代码

[异步微任务]

  • requestAnimalFrame
  • Promise.then/catch/finally
  • async/await
  • queueMicrotask 手动创建一个异步的微任务
  • MutationObserver 监听当前DOM元素属性值改变
  • IntersectionObserver 监听当前DOM元素和可视窗口
  • process.nextTick[Node]

[异步宏任务]

  • setTimeout/setInterval
  • 事件绑定/队列(addEventListener)
  • XMLHttpRequest/Fetch
  • MessageChannel(消息队列)
  • setImmediate[Node]

js完成异步操作: 借用浏览器的多线程,再基于EventLoop事件循环机制,实现的单线程异步效果

setTimeout(() => {
console.log(1);
}, 0);
也不是立即执行定时器,而是要等待5-7ms[浏览器最快处理时间]

setTimeout(() => {
    console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
    console.log(3);
}, 10);
console.log(4);
console.time('AA');
for(let i = 0; i < 90000000; i++) {
    //do something 80ms左右
}
console.log(5);
setTimeout(() => {
    console.log(6);
}, 8);
console.log(7);
setTimeout(() => {
    console.log(8);
}, 15);
console.log(9);

// 245793168

EventLoop事件循环机制

浏览器加载页面,除了开辟堆栈内存外,还创建了两个队列

  • WebAPI 任务监听队列
  • EventQueue 事件/任务队列
    首先:当主线程自上而下执行代码过程当中,如果遇到“异步代码”
    把异步任务放到WebAPI中去监听!
  • 浏览器开辟新的线程去监听是否可以执行
  • 不会阻碍主线程的渲染,会继续执行同步的代码
    其次:当异步任务被监测为可以执行了,也不会立即去执行,而是把其挪至到EventQueue中排队等待执行。
  • 根据微任务宏任务放在不同的队列中
  • 谁先进来排队的,谁在各自队伍的最前面
    PS:对于定时器来讲,设定一个等待时间,到时间后不一定会立即去执行,只有同步代码执行完。
    最后,当“同步代码(同步宏任务)”执行完毕(有时把同步任务也当做宏任务,宏任务就分为同步宏任务和异步宏任务),主线程空闲下来,此时会去EventQueue中把正在排队的异步任务,按照顺序取出来执行。
  • 异步微任务优先级比较高,不论其任务是先放入的还是后放入的。只要有可执行的异步微任务,永远先执行它!
  • 同样级别的任务,是谁先放入[谁先到可执行],谁先执行。
  • 要把任务拿到栈中执行,而且是交给主线程去执行,所以只要这个拿出来的任务没有执行完,也不会再去拿其他的任务。(代码执行只能放栈里执行)

情况一:

p.then(onfullfilled,onrejected),已知实例p的状态和值,也不会立即把onfullfilled/onrejected执行,而是创建“异步微任务”先进入WebAPI中,发现状态是成功,则onfullfilled可以被执行,再把其挪至EventQueue中排队等着。

let p = new Promise((resolve) => {
    resolve(10);
})
p.then((value) => {
    console.log('成功', value);
})
console.log(1);

情况二:

如果还不知道实例p的状态,则先把Onfullfilled/Onrejected存储起来[理解为:进入WebAPI去监听,只有知道实例的状态,才知道谁可以执行];定时器到时间,resolve/reject执行,立即修改实例的状态和值,也决定了WebAPI中监听的方法(onfulfilled/onrejected)哪一个去执行,挪至到EventQueue中,异步微任务队列。等待其他同步代码执行完,再拿出来执行。

let p = new Promise((resolve) => {// executor函数立即执行
    setTimeout(() => { //把定时器放到WebAPI中监听 等一秒之后放入EventQueue 异步宏任务
        resolve(10);//立即把状态转为成功
        console.log(p, 2);//2
    }, 1000);
})
p.then((value) => {//还不知道p的状态,先把方法存起来等价于放WebAPI去监听  知道状态之后才能决定执行Onfullfilled还是Onrejected
    console.log('成功', value);
})
console.log(1);//输出1
// 1 2 成功 10

情况三:

Promise.resolve(1)
  .then(value => {
    console.log('成功', value);
    return 2;
  })
  .then(value => {
    console.log('成功', value);
  })
console.log('外面');
//外面 成功1 成功2

遇到await

  • 立即执行其后面代码,看返回的promise实例(如果不是promise实例也会变为promise实例) 是否为成功
  • 会把当前上下文中,await下面代码当做异步的微任务
    • 进入WebAPI去监听:只有后面实例的状态是成功的,才可以执行
    • 可执行则进入到EventQueue中排队等待
const fn = async () => {
    console.log(1);
    return 10;//返回状态成功 值为10 的实例
}
(async function() {
    let result = await fn();
    console.log(2, result);//异步微任务 等待await后面的实例 状态为成功 才可以执行  失败不可以执行
})();
console.log(3);
//1 3 2 10
async function async1() {
    console.log('async1 start');//2
    await async2();
    console.log('async1 end');//微1 微任务列表 6
}
async function async2() {
    console.log('async2');//3
    //默认返回状态为成功 值为undefined的实例
}
console.log('script start');//1
setTimeout(function() {
    console.log('settimeout'); //8
}, 0);//宏1 宏任务列表
async1();
new Promise(function(resolve) {//executor函数立即执行
    console.log('promise1');//4
    resolve();//成功
}).then(function() {
    console.log('promise2');//微2 微任务队列 7
});
console.log('script end');//5

练习题1:

console.log('start');//1
let intervalId;
Promise.resolve().then(() => {
    console.log('p1');//微1 异步微任务队列1 //2
}).then(() => {
    console.log('p2');//微2 异步微任务队列2 //3
})
setTimeout(() => {//宏1 异步宏任务1 //进入栈内存 主线程
    Promise.resolve().then(()=>{
        console.log('p3');//微3 异步微任务队列3 5
    }).then(() => {
        console.log('p4');//微4 异步微任务队列4 6
    });
    intervalId = setInterval(() => { //宏2 异步宏任务2
        console.log('interval'); // 7
    }, 3000);
    console.log('timeout');//4
}, 0);

练习题2

setTimeout(() => {
    console.log('a');//宏1 4
});
Promise.resolve().then(()=>{
    console.log('b');//微1 监听实例的状态 理解为fullfilled 放入微任务队列中  1
}).then(() => {//微2 监听实例p1的状态 监听到p1的状态 放入微任务队列中
    return Promise.resolve('c').then(data => {//微3 立即放入微任务队列中
        setTimeout(() => {
            console.log('d');//宏2放入宏任务队列 5
        });
        console.log('f');// 2
        return data;//得到p2实例状态 c
    });
}).then(data => {//微3 监听实例p2的状态 
    console.log(data);//3 c
})
// b f c a d

练习题3

function func1() {
    console.log('func1 start');//3 8
    return new Promise(resolve => {
        resolve('OK');
    });
}
function func2() {
    console.log('func2 start');//4
    return new Promise(resolve => {
        setTimeout(() => {//宏2 等待10ms后
            resolve('OK');
        }, 10);
    });
}
console.log(1);//1
setTimeout(async () => {//宏1 等待20ms后放入 异步宏任务队列中 宏1
    console.log(2);//7
    await func1(); 
    console.log(3);//9
}, 20);
for(let i = 0; i < 90000000; i++) {}
console.log(4);//2
func1().then(result => {//微1 等待func1确定实例状态 放入微任务队列中
    console.log(5);//6
})
func2().then(result => {//微2 等待func2实例状态
    console.log(6);//11
});
setTimeout(() => {//宏3 等待5-7ms放入 异步宏任务队列
    console.log(7);//10
}, 0);
console.log(8);//5
//1 4 func1 func2 8 5 2 func1 3 7 6

练习题4

async function async1() {
    console.log('async1 start')//2
    await async2()
    console.log('async1 end')//微1 等待await后面的状态  放入异步微任务中 6
}
async function async2() {
    console.log('async2')//3
}
console.log('script start')//1
setTimeout(function () {//宏1
    console.log('settimeout')//8
})
async1()
new Promise(function (resolve) {
    console.log('promise1')//4
    resolve()
}).then(function () {//微2 等待实例状态
    console.log('promise2')//7
})
console.log('script end')//5
// script start
// async1 start
// async2      
// promise1    
// script end  
// async1 end  
// promise2    
// settimeout

练习题5

let body = document.body;
body.addEventListener('click', function() {//宏1
    Promise.resolve().then(() => {//微1 检测实例状态为成功再执行 异步微任务队列1
        console.log(1);//2 先执行微1 再执行宏2
    });
    console.log(2);//1
});
body.addEventListener('click', function() {//宏2
    Promise.resolve().then(() => {//微2
        console.log(3);
    });
    console.log(4);
})
//2 1 4 3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值