浏览器原理-事件循环机制

2020-02-26  本文已影响0人  9吧和9说9话

浏览器的渲染进程的主线程承担着大量的任务:DOM解析,javascript的解析执行,样式计算、布局渲染,接收用户事件等等。那么是如何工作的呢?

事件循环机制

  1. 主线程不断的循环处理一个消息任务队列
  2. IO线程负责接收其他的线程以及其他的进程发送过来的任务。
  3. 为了保证某些任务的特殊需求:实时性和主线程的执行效率,每一个消息队列中的任务都有一个微任务队列, 例如我们的promisemutationObserver都会被添加到微任务队列中。这样在当前的任务执行完成之前,会触发自己微任务队列的处理。避免了直接将微任务添加到消息队列队尾造成的实时性不足的问题。
  4. 如何安全的推出当前主线程:chrome是在确定推出当前页面之前,设置了一个标记变量,主线程循环中每次执行完任务就会判断当前的标记是否为真,如果是就跳出循环,结束线程执行。
  5. 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();
}
上一篇 下一篇

猜你喜欢

热点阅读