进程与线程
-
单线程Js怎么实现异步的
通过浏览器内核多线程实现异步 -
进程
1、cpu资源分配的最小单位
2、程序运行的实例
3、同一程序可以产生多个进程
4、一个进程包含一个或者多个线程 -
线程
1、操作系统能够进行运算调度的最小单位
2、一次只能执行一个任务
3、有自己的调用栈、寄存器环境
4、同一进程的线程共享资源分享 -
浏览器内核进程、线程
| 线程名 | 描述 |
|---|---|
| GUI线程 | 渲染布局 |
| JS引擎线程 | 解析、执行JS与GUI互斥线程 |
| 定时器触发线程 | setTimeout setInterval |
| 事件触发线程 | 将满足触发条件的事件放入任务队列 |
| 异步HTTP请求线程 | XHR所在线程 |
异步队列
- JavaScript的执行顺序
setTimeout(() => {
console.log('s1');
new Promise((resolve,reject)=>{
resolve();
}).then(function(){
new Promise((resolve,reject)=>{
resolve();
}).then(function(){
console.log('t4');
})
console.log('t2');
})
}, 0);
new Promise(function(resolve){
console.log('p1');
resolve();
}).then(function(){
console.log('t1');
})
setTimeout(function(){
console.log('s2');
})
console.log(2);
// 分析代码执行书序
// 1、将第7行的setTimeout入宏队列,执行到21行,打印出p1,将23行then方法入微队列,将26行settimeout入宏队列,打印2 这次打印:p1 2
// 2、询问异步队列中的微队列,执行24行的微任务,打印出t1,微队列清空了 这次打印 t1
// 3、询问异步队列中的宏任务,执行第7行的setTimeout,打印出s1 将第11行then入微队列 这次打印s1
// 4、微队列不为空,执行第11汗微队列,打印出t2,将15行入微队列 这次打印t2
// 5、微队列不为空,执行第15汗微队列,打印出t4 这次打印t4
// 6、微队列清空了,执行第26行的宏任务,打印出 s2 这次打印s2
// 7、异步队列执行完毕,程序执行完毕
// 结果为:p1 2 t1 s1 t2 t4 s2
- 宏任务与微任务
- 微任务:Promise.then、 process.nextTick、Object.observe、MutationObserver、postMessage
- 宏任务:整体代码script、setTimeout、setInterval、setImmediate、I/O、UI Rendering
异步场景
1、定时器
2、网络请求
3、事件绑定
4、ES6 Promise
- 定时器异步任务原理
1、调用webAPI
2、定时器线程计数2s
3、事件触发线程将定时器事件放入任务队列
4、主线程通过Event Loop遍历任务队列
5、任务队列是由事件触发线程往里面加,由Event Loop往外推
-
定时器问题
1、定时器任务可能不会按时执行
2、定时器嵌套5次之后最小间隔不能低于4ms -
定时器应用场景
1、防抖
2、节流
3、倒计时
4、动画
常见的一个关于块级作用域和定时器的问题
for (var i = 0; i <= 10; i++) {
setTimeout(() => {
console.log(i);
}, 1000 * i);
}
//打印出 11 11 11 11 11 11 11 11 11 11 11
for (let i = 0; i <= 10; i++) {
setTimeout(() => {
console.log(i);
}, 1000 * i);
}
//打印出 0 1 2 3 4 5 6 7 8 9 10 11
for (var i = 0; i <= 10; i++) {
(function(i) {
setTimeout(() => {
console.log(i);
}, 1000 * i);
})(i);
}
//打印出 0 1 2 3 4 5 6 7 8 9 10 11
Event Loop机制
浏览器端
- 异步事件
- 宏观:浏览器多线程
- 微观:Event Loop,时间循环;
async function aync1(){
console.log('async1 start')
await async2();
console.log('async1 end');
}
async function async2(){
return Promise.resolve().then(()=>{
console.log('async2 promise');
})
}
console.log('start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve){
console.log('promise1')
}).then(function(){
console.log('promise2')
});
// 1、同步代码块先执行,打印出start;
// 2、执行到12行时,将该函数放到宏任务;
// 3、执行到第15行的时候,打印出async1 start;然后调用async2,将第8行放到微任务,第4行会在第8行执行完以后立即执行;
// 4、执行到第16行打印出promise1,将第19行放到微任务队列;
// 5、目前打印出的是:start async1 start promise1;
// 6、查看微任务队列,执行第8行和第19行,打印出: async2 promise promise2 aync1 end;
// 7、调用第12行和4行的宏任务,分别打印出:setTimeout
// 8、最后打印结果为:start 'async1 start' promise1 'async2 promise' promise2 'aync1 end' setTimeout
Node端
- 六个阶段
- timers:执行timer的回调;
- pending callbacks:系统操作的回调;
- idle、prepare:内部使用;
- poll:等待新I/O事件;
- check:执行setImmediate回调;
- close callbacks:内部使用;
- poll阶段的两个主要功能:
- 计算应该被block多久;
- 处理poll队列的事件;
const fs = require('fs');
function someAsyncOperation(callback){
fs.readFile(__dirname,callback);
}
const timeoutScheduled = Date.now();
setTimeout(() => {
const delay = Date.now()-timeoutScheduled;
console.log(`${delay}ms have passed since I was scheduled`);
}, 100);
someAsyncOperation(()=>{
const startCallback = Date.now();
while (Date.now()-startCallback<200) {
//do nothing
}
});
输出结果图:
- 首先执行fs.readFile,花费时间2ms;
- 再执行callback,花费时间200ms;
- setTimeout时间到了并执行,所以打印出了202ms;
- process.nextTick() 是一个异步的nodeAPI,但不属于event loop的阶段,node执行到该方法时event loop停下来,立即执行nextTick中的内容
const fs = require('fs');
fs.readFile(__filename,_=>{
setTimeout(() => {
console.log('setTimeout')
}, 0);
setImmediate(_ =>{
console.log('setImmediate');
process.nextTick(_ =>{
console.log('nextTick2')
})
})
process.nextTick(_ =>{
console.log('nextTick1')
})
})