浏览器原理-事件循环机制
2020-02-26 本文已影响0人
9吧和9说9话
浏览器的渲染进程的主线程承担着大量的任务:DOM
解析,javascript
的解析执行,样式计算、布局渲染,接收用户事件等等。那么是如何工作的呢?
事件循环机制
- 主线程不断的循环处理一个消息任务队列
- IO线程负责接收其他的线程以及其他的进程发送过来的任务。
- 为了保证某些任务的特殊需求:实时性和主线程的执行效率,每一个消息队列中的任务都有一个微任务队列, 例如我们的
promise
和mutationObserver
都会被添加到微任务队列中。这样在当前的任务执行完成之前,会触发自己微任务队列的处理。避免了直接将微任务添加到消息队列队尾造成的实时性不足的问题。 - 如何安全的推出当前主线程:chrome是在确定推出当前页面之前,设置了一个标记变量,主线程循环中每次执行完任务就会判断当前的标记是否为真,如果是就跳出循环,结束线程执行。
-
setTimeout
是如何执行的:有一个延时任务列表(内部结构是HashMap),用来存放需要延时执行的任务。
为啥不能直接将延时任务放到消息队列中呢:因为消息队列是按照顺序执行的,无法做到在指定的延时时间之后执行任务。
延时任务列表是怎么做到的呢
延时任务列表中的任务结构大致如下:
// 记录了当前执行的任务、开始计时时间、延时时间、以及任务ID(就是我们的setTimeout的返回值)
struct DelayTask{
int64 id;
CallBackFunction cbf;
int start_time;
int delay_time;
};
DelayTask timerTask;
timerTask.cbf = showName;
timerTask.start_time = getCurrentTime(); //获取当前时间
timerTask.delay_time = 200;//设置延迟执行时间
在主线程循环处理消息队列消息中,处理完成当前的任务之后(包括当前任务的微任务),就会执行延时任务触发检测,查看当前延时任务列表中的任务哪些达到了延时时间间隔,就会执行这些延时任务。
void ProcessTimerTask(){
//从delayed_incoming_queue中取出已经到期的定时器任务
//依次执行这些任务
}
TaskQueue task_queue;
void ProcessTask();
bool keep_running = true;
void MainTherad(){
for(;;){
//执行消息队列中的任务
Task task = task_queue.takeTask();
ProcessTask(task);
//执行延迟队列中的任务
ProcessDelayTask()
if(!keep_running) //如果设置了退出标志,那么直接退出线程循环
break;
}
}
事件循环机制 图片来源:极客时间
XMLHttpRequest
是如何执行的
XMLHttpRequest 工作流程图 图片来源:极客时间
代码示例:
function GetWebData(URL){
/**
* 1:新建XMLHttpRequest请求对象
*/
let xhr = new XMLHttpRequest()
/**
* 2:注册相关事件回调处理函数
*/
xhr.onreadystatechange = function () {
switch(xhr.readyState){
case 0: //请求未初始化
console.log("请求未初始化")
break;
case 1://OPENED
console.log("OPENED")
break;
case 2://HEADERS_RECEIVED
console.log("HEADERS_RECEIVED")
break;
case 3://LOADING
console.log("LOADING")
break;
case 4://DONE
if(this.status == 200||this.status == 304){
console.log(this.responseText);
}
console.log("DONE")
break;
}
}
xhr.ontimeout = function(e) { console.log('ontimeout') }
xhr.onerror = function(e) { console.log('onerror') }
/**
* 3:打开请求
*/
xhr.open('Get', URL, true);//创建一个Get请求,采用异步
/**
* 4:配置参数
*/
xhr.timeout = 3000 //设置xhr请求的超时时间
xhr.responseType = "text" //设置响应返回的数据格式
xhr.setRequestHeader("X_TEST","time.geekbang")
/**
* 5:发送请求
*/
xhr.send();
}