• 消息系统


    一个tab有几个进程

    1. 浏览器进程,负责页面显示,用户交互,子进程管理等
    2. 渲染进程,将html,css,js转换为网页
    3. GPU进程,用来渲染UI界面
    4. 网络进程,负责网页资源加载
    5. 插件进程,主要负责插件的运行

    进程,线程,协程(纤程)

    1. 一个进程就是一个程序的运行实例

    2. 线程是依附进程的,而进程中的多线程并行处理能提高运算效率

    3. 进程中的任意线程执行出错,都会导致整个进程崩溃

    4. 线程之间共享进程中的数据

    5. 当一个进程关闭之后,操作系统会回收进程所占用的内存

    6. 进程之间内容是相互隔离的

    7. 协程是一种比线程更轻量级的存在,协程不是被操作系统内核所管理,完全由程序控制(也就是在用户态执行)不会像线程切换那样消耗资源!

    消息队列和事件循环

    1. 消息队列是先进先出的执行方式如何处理高优先级的任务,当修改了DOM后如何即时更新,立即执行会影响当前任务的执行时长,以异步消息添加到队尾会影响实时性,消息队列中的任务称为宏任务,每个宏任务中包含了微任务队列,在执行宏任务过程中,如果DOM有变化,那么就会将该变化添加到微任务列表中,这样就不会影响到宏任务的执行,等宏任务主要功能执行完后,执行当前宏任务的微任务,这样就解决了执行效率和实时性问题
    2. 如何解决单个任务执行时长过久问题,通过回调来让js任务滞后执行

    setTimeout缺陷

    1. 执行不精确,由于同步任务执行过长,setTimeout被分配到了另一个task去执行,setTimeout回调被放入消息队列中并不是立即执行,要等当前任务执行完
    
    function bar() {
        console.log('bar')
    }
    function foo() {
        setTimeout(bar, 0);
        for (let i = 0; i < 5000; i++) {
            let i = 5+8+8+8
            console.log(i)
        }
    }
    foo()
    

    1. 嵌套调用时,调用超过5次以上,后面每次调用的间隔时间为4ms
    
    function cb() { setTimeout(cb, 0); }
    setTimeout(cb, 0);
    
    1. 未激活的页面,setTimtout执行最小间隔是1000ms

    2. 延时执行时间有最大值,32bit,只有31bit表示数,另外1bit表示符号位

    function showName(){
      console.log("aaa")
    }
    var timerID = setTimeout(showName,2147483648);//会被理解调用执行
    
    1. this指向window
    
    var name= 1;
    var MyObj = {
      name: 2,
      showName: function(){
        console.log(this.name);
      }
    }
    setTimeout(MyObj.showName,1000)
    

    XMLHttpRequest运行机制

    宏任务与微任务

    1. 宏任务控制不精确,2个任务之间添加了其他的系统任务,如果添加的任务执行的时间过长,会影响到后面任务的执行
    
    <!DOCTYPE html>
    <html>
        <body>
            <div id='demo'>
                <ol>
                    <li>test</li>
                </ol>
            </div>
        </body>
        <script type="text/javascript">
            function timerCallback2(){
              console.log(2)
            }
            function timerCallback(){
                console.log(1)
                setTimeout(timerCallback2,0)
            }
            setTimeout(timerCallback,0)
        </script>
    </html>
    

    异步回调带来的问题

    1. 异步回调代码逻辑不连续
    
    //执行状态
    function onResolve(response){console.log(response) }
    function onReject(error){console.log(error) }
    
    let xhr = new XMLHttpRequest()
    xhr.ontimeout = function(e) { onReject(e)}
    xhr.onerror = function(e) { onReject(e) }
    xhr.onreadystatechange = function () { onResolve(xhr.response) }
    
    //设置请求类型,请求URL,是否同步信息
    let URL = 'https://xxx.com'
    xhr.open('Get', URL, true);
    
    //设置参数
    xhr.timeout = 3000 //设置xhr请求的超时时间
    xhr.responseType = "text" //设置响应返回的数据格式
    xhr.setRequestHeader("X_TEST","time.geekbang")
    
    //发出请求
    xhr.send();
    

    改造一下使其变得线性

    
    //makeRequest用来构造request对象
    function makeRequest(request_url) {
        let request = {
            method: 'Get',
            url: request_url,
            headers: '',
            body: '',
            credentials: false,
            sync: true,
            responseType: 'text',
            referrer: ''
        }
        return request
    }
    
    
    //[in] request,请求信息,请求头,延时值,返回类型等
    //[out] resolve, 执行成功,回调该函数
    //[out] reject  执行失败,回调该函数
    function XFetch(request, resolve, reject) {
        let xhr = new XMLHttpRequest()
        xhr.ontimeout = function (e) { reject(e) }
        xhr.onerror = function (e) { reject(e) }
        xhr.onreadystatechange = function () {
            if (xhr.status = 200)
                resolve(xhr.response)
        }
        xhr.open(request.method, URL, request.sync);
        xhr.timeout = request.timeout;
        xhr.responseType = request.responseType;
        //补充其他请求信息
        //...
        xhr.send();
    }
    
    
    XFetch(makeRequest('https://xxx.org'),
        function resolve(data) {
            console.log(data)
        }, function reject(e) {
            console.log(e)
        })
    
    1. 接口依赖形成回调地狱
    
    XFetch(makeRequest('https://aaa.org/?category'),
          function resolve(response) {
              console.log(response)
              XFetch(makeRequest('https://bbb.org/column'),
                  function resolve(response) {
                      console.log(response)
                      XFetch(makeRequest('https://ccc.org')
                          function resolve(response) {
                              console.log(response)
                          }, function reject(e) {
                              console.log(e)
                          })
                  }, function reject(e) {
                      console.log(e)
                  })
          }, function reject(e) {
              console.log(e)
          })
    
    1. Promise消灭嵌套,合并多个任务的错误处理
    
    function XFetch(request) {
      function executor(resolve, reject) {
          let xhr = new XMLHttpRequest()
          xhr.open('GET', request.url, true)
          xhr.ontimeout = function (e) { reject(e) }
          xhr.onerror = function (e) { reject(e) }
          xhr.onreadystatechange = function () {
              if (this.readyState === 4) {
                  if (this.status === 200) {
                      resolve(this.responseText, this)
                  } else {
                      let error = {
                          code: this.status,
                          response: this.response
                      }
                      reject(error, this)
                  }
              }
          }
          xhr.send()
      }
      return new Promise(executor)
    }
    
    
    var x1 = XFetch(makeRequest('https://aaa.org/?category'))
    var x2 = x1.then(value => {
        console.log(value)
        return XFetch(makeRequest('https://bbb.org/column'))
    })
    var x3 = x2.then(value => {
        console.log(value)
        return XFetch(makeRequest('https://ccc.org'))
    })
    x3.catch(error => {
        console.log(error)
    })
    

    Promise通过回调函数延迟绑定,回调函数返回值穿透和错误冒泡解决了问题

    
    //创建Promise对象x1,并在executor函数中执行业务逻辑
    function executor(resolve, reject){
        resolve(100)
    }
    let x1 = new Promise(executor)
    
    
    //x1延迟绑定回调函数onResolve
    function onResolve(value){
        console.log(value)
    }
    x1.then(onResolve)
    
    

    
    function executor(resolve, reject) {
        let rand = Math.random();
        console.log(1)
        console.log(rand)
        if (rand > 0.5)
            resolve()
        else
            reject()
    }
    var p0 = new Promise(executor);
    
    var p1 = p0.then((value) => {
        console.log("succeed-1")
        return new Promise(executor)
    })
    
    var p3 = p1.then((value) => {
        console.log("succeed-2")
        return new Promise(executor)
    })
    
    var p4 = p3.then((value) => {
        console.log("succeed-3")
        return new Promise(executor)
    })
    
    p4.catch((error) => {
        console.log("error")
    })
    console.log(2)
    
    1. async await 用线性的书写方式,不阻塞主线程的方式来写异步代码
    
    fetch('https://aaa.org')
          .then((response) => {
              console.log(response)
              return fetch('https://bbb.org/test')
          }).then((response) => {
              console.log(response)
          }).catch((error) => {
              console.log(error)
          })
    
    
    async function foo(){
      try{
        let response1 = await fetch('https://www.geekbang.org')
        console.log('response1')
        console.log(response1)
        let response2 = await fetch('https://www.geekbang.org/test')
        console.log('response2')
        console.log(response2)
      }catch(err) {
           console.error(err)
      }
    }
    foo()
    

    1. generator 协程可以暂停和恢复执行的特点,用同步的方式来写异步代码
    
    function* genDemo() {
        console.log("开始执行第一段")
        yield 'generator 2'
    
        console.log("开始执行第二段")
        yield 'generator 2'
    
        console.log("开始执行第三段")
        yield 'generator 2'
    
        console.log("执行结束")
        return 'generator 2'
    }
    
    console.log('main 0')
    let gen = genDemo()
    console.log(gen.next().value)
    console.log('main 1')
    console.log(gen.next().value)
    console.log('main 2')
    console.log(gen.next().value)
    console.log('main 3')
    console.log(gen.next().value)
    console.log('main 4'
    
    

    
    async function foo() {
        console.log(1)
        let a = await 100
        console.log(a)
        console.log(2)
    }
    console.log(0)
    foo()
    console.log(3)
    

    异步回调执行过程

    1. UI线程会从消息队列中取出一个任务,并分析该任务
    2. 分析过程发现该任务是一个下载请求,那么主线程会将该任务交给网络线程去执行
    3. 网络线程接到请求之后,便会和服务器服务器端建立连接,并发出下载请求
    4. 网络线程不断的收到服务器端传过来的数据
    5. 网络线程每次接收到数据时,都会将设置的回调函数和返回的数据信息,如大小,返回了多少字节,返回的数据存在内存中存在的位置封装成一个新事件,并将该事件放到消息队列中
    6. UI线程继续循环的读取消息队列中的事件,如果是下载状态的事件,那么UI线程会执行回调函数
    7. 接收到下载结束事件,UI线程会显示该页面下载完成

    永不爆栈的宏任务

    
    function foo() {
      setTimeout(foo, 0)
    }
    foo()
    

    函数内部触发的宏任务比函数内部触发的微任务优先

    
    function bar(){
      console.log('bar')
      Promise.resolve().then(
        (str) =>console.log('micro-bar')
      ) 
      setTimeout((str) =>console.log('macro-bar'),0)
    }
    
    
    function foo() {
      console.log('foo')
      Promise.resolve().then(
        (str) =>console.log('micro-foo')
      ) 
      setTimeout((str) =>console.log('macro-foo'),0)
      
      bar()
    }
    foo()
    console.log('global')
    Promise.resolve().then(
      (str) =>console.log('micro-global')
    ) 
    setTimeout((str) =>console.log('macro-global'),0)
    
    

    永不执行的协程

    
    function NeverResolvePromise(){
        return new Promise((resolve, reject) => {})
    }
    async function getResult() {
        let a = await NeverResolvePromise()
        console.log(a)
    }
    getResult()
    console.log(0)
    

    
    function* getResult() {
    
    yield 'getUserID'
    
    yield 'getUserName'
    
    return 'name'
    
    }
    
    let result = getResult()
    
    console.log(result.next().value)
    
    console.log(result.next().value)
    console.log(result.next().value)
    

    
    function HaveResolvePromise(){
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(100)
              }, 0);
          })
    }
    async function getResult() {
        console.log(1)
        let a = await HaveResolvePromise()
        console.log(a)
        console.log(2)
    }
    console.log(0)
    getResult()
    console.log(3)
    
    

    一帧内合理分配任务

    浏览器每一帧都需要完成哪些任务

    1. 处理用户的交互
    2. js解析执行
    3. 帧开始,窗口尺寸变更
    4. rAF
    5. 布局绘制

    刷新率

    1. 表示每秒钟更新多少次显示器的屏幕图像,60Hz,每秒60张画面,刷新率越高画面越流畅

    帧率

    1. 每秒由显卡渲染生成的静态图像数量,单位是FPS,帧率越高,画面更顺畅

    帧率与刷新率

    1. 刷新率与帧数不匹配就会出现卡断
    2. 60Hz页面要想不卡顿,1s就要执行60帧(FPS)与刷新率保持一致,每一帧1000/60 = 16ms,每一帧就需要控制在16ms内完成!

    如果渲染进程生成的帧速比屏幕的刷新率慢,那么屏幕会在两帧中显示同一个画面,如果持续发生,用户就会感到明显的卡顿

    如果渲染进程生成的帧速率实际上比屏幕刷新率快,比如当帧速率在 100fps 而刷新率只有 60Hz 的时候,GPU 所渲染的图像并非全都被显示出来,这就会造成丢帧现象。

    setTimeout,setInterval无法准确执行

    1. 没有在16ms完成,帧率低于刷新率,造成2个画面绘制一帧造成卡顿
    2. setTimeout是由用户控制的,绘制时机很难和VSync时钟保持一致
    3. 还会出现丢帧现象

    requestAnimationFrame

    1. 它由系统决定回调函数的执行时机,回请求浏览器在下一次重新渲染页面之前执行回调函数,无论设备的刷新率是多少,它会紧跟屏幕刷新一次所需要的时间,例如某一设备的刷新率是 75 Hz,那这时的时间间隔就是 13.3 ms(1 秒 / 75 次),但是也要注意在函数内不要做太多任务,如果太多同步阻塞代码,也会让任务的执行时间变长而掉帧
    2. requestAnimationFrame用来和VSync时钟保持一致
    3. 基本稳定在16ms没有掉帧的现象

    requestIdleCallback


    1. 如果一帧内有空闲,会执行
    requestIdleCallback(myNonEssentialWork, { timeout: 2000 });
    ​
    // 任务队列
    const tasks = [
     () => {
       console.log("第一个任务");
     },
     () => {
       console.log("第二个任务");
     },
     () => {
       console.log("第三个任务");
     },
    ];
    ​
    function myNonEssentialWork (deadline) {
     // 如果帧内有富余的时间,或者超时
     while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && tasks.length > 0) {
       work();
     }
    ​
     if (tasks.length > 0)
       requestIdleCallback(myNonEssentialWork);
     }
    ​
    function work () {
     tasks.shift()();
     console.log('执行任务');
    }
    
  • 相关阅读:
    Python3基础 dict get 在查询不存在的键时,返回指定的内容
    MySql和Sql的单行注释和多行注释的区别
    MySql Server 5.7的下载及安装详细步骤
    sql Server中临时表与数据表的区别
    Sql Server中集合的操作(并集、差集、交集)学习
    sql server2008 如何获取上月、上周、昨天、今天、本周、本月的查询周期(通过存储过程)
    sql server中的大数据的批量操作(批量插入,批量删除)
    sql Server如何执行批量插入和批量删除
    vs2015 企业版、专业版如何破解(秘钥)
    Dos命令下目录操作
  • 原文地址:https://www.cnblogs.com/pluslius/p/14958340.html
Copyright © 2020-2023  润新知