最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 浏览器事件循环Event Loop

    正文概述 掘金(盘古文化)   2021-01-25   532

    1.关于javascript

    javascript是一门单线程语言,在最新的HTML5中提出了Web-Worker,但javascript是单线程这一核心仍未改变。所以一切javascript版的"多线程"都是用单线程模拟出来的,一切javascript多线程都是纸老虎!(不管是什么新框架新语法糖实现的所谓异步,其实都是用同步的方法去模拟的)

    2.javascript事件循环

    事件循环是js实现异步的一种方法,也是js的执行机制。

    首先浏览器会把主任务队列中的同步任务挨个全部执行完,然后再去等待任务队列中看哪个任务可以执行了, 然后把该执行的任务放到主任务队列中去执行,等这个任务执行完, 再去等待任务中看谁可以执行了,再把这个任务放到主任务队列中执行... 如此循环。 这种循环叫做事件循环(Event Loop) js是单线程,js任务也要一个一个顺序执行。如果一个任务耗时过长,那么后一个任务也必须等着。那么问题来了,假如我们想浏览新闻,但是新闻包含的超清图片加载很慢,难道我们的网页要一直卡着直到图片完全显示出来?因此聪明的程序员将任务分为两类:

    1. 同步任务
    2. 异步任务

    一张图表示事件循环 浏览器事件循环Event Loop

    1. 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
    2. 当指定的事情完成时,Event Table会将这个函数移入Event Queue。
    3. 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
    4. 上述过程会不断重复,也就是常说的Event Loop(事件循环)。

    主线程执行栈何时为空?

    js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。

    所以可以看做是这样的:

    1. 浏览器线程先执行同步任务,途中遇到异步任务就将其加入到等待任务队列中去,然后继续向下执行,
    2. 等同步任务全部执行完毕后,再去等待任务队列中去将所有可执行的微任务逐个执行,
    3. 执行完微任务后在拿取第一个先到达执行条件的宏任务来执行,
    4. 执行完后再去等待任务队列中清理执行完所有已到达执行条件的微任务,
    5. 然后再拿取下一个宏任务来执行,如果宏任务执行产生微任务或者微任务执行产生宏任务就一样加入到等待任务队列中,然后还是按照主线程每次到等待队列中先执行完所以的微任务再逐个执行宏任务的顺序来走

    异步任务都是谁先到达条件谁先执行,但是谁先到达执行条件也有优先级的问题,这个优先级要看这个任务是宏任务还是微任务;微任务的优先级比宏任务的要高;

    3.运行机制

    在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下:

    1. 执行一个宏任务(栈中没有就从事件队列中获取)
    2. 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
    3. 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
    4. 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
    5. 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

    浏览器事件循环Event Loop

    宏任务:

    (macro)task,可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。 浏览器为了能够使得JS内部(macro)task与DOM任务能够有序的执行,会在一个(macro)task执行结束后,在下一个(macro)task 执行开始前,对页面进行重新渲染,流程如下:

    (macro)task->渲染->(macro)task->...

    微任务:

    microtask,可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前task任务后,下一个task之前,在渲染之前。 所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染。也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask都执行完毕(在渲染前)。

    总结

    主线程的任务队列里面,会先执行一次宏任务,然后宏任务中产生的微任务会放到微任务队列中,而遇到宏任务时,会

    -宏任务(macrotask)微任务(microtask)
    谁发起的宿主(Node、浏览器)JS引擎具体事件1.script(同步代码), 2.setTimeout/setInterval, 3.UI rendering/UI事件, 4.postMessage,MessageChannel, 5.setImmediate,I/O(Node.js)1. Promise, 2.MutaionObserver, 3.Object.observe(已废弃;Proxy 对象替代), 4.process.nextTick(Node.js)谁先运行后运行先运行会触发新一轮tick吗不会
    setTimeout(function(){
    console.log('1')
    });
    new Promise(function(resolve){
      console.log('2');
      resolve();
    }).then(function(){
    console.log('3')
    });
    console.log('4');//2  4  3  1
    

    分析:

    1. settimeout是宏任务,虽然先执行的他,但是他被放到了宏任务的eventqueue里面
    2. 往下检查看有没有微任务,发现Promise回调函数内的代码是同步的(微任务)输出2
    3. then函数把他放入了微任务序列。
    4. 主代码块(宏任务)输出4
    5. 主线进程所有代码执行结束。先从微任务queue里拿回掉函数,输出3微任务全部完成
    6. 再从宏任务的queue拿函数。输出1
    console.log('1');
    setTimeout(function() {
        console.log('2');
        process.nextTick(function() {
            console.log('3');
        })
        new Promise(function(resolve) {
            console.log('4');
            resolve();
        }).then(function() {
            console.log('5')
        })
    })
    process.nextTick(function() {
        console.log('6');
    })
    new Promise(function(resolve) {
        console.log('7');
        resolve();
    }).then(function() {
        console.log('8')
    })
    setTimeout(function() {
        console.log('9');
        process.nextTick(function() {
            console.log('10');
        })
        new Promise(function(resolve) {
            console.log('11');
            resolve();
        }).then(function() {
            console.log('12')
        })
    })
    
    1. 主代码块输出1
    2. 宏任务1,setTimeout(2,3,4,5)
    3. 微任务1process(6)
    4. promise立即执行,回调是同步输出7,then微任务2(8)
    5. 宏任务2setTimeout(9,10,11,12),主代码全部执行完(余微任务1,2,宏任务1,2)
    6. 执行微任务1,输出6
    7. 执行微任务2,输出8 ,微任务全部执行完(余宏任务1, 2)
    8. 执行宏任务1,输出2,增微任务process(3),promise立即执行回调输出4,微任务(5),
    9. 执行微任务输出3,输出5
    10. 执行宏任务2,输出9,增微任务process(10),promise立即执行回调输出11,微任务(12),
    11. 执行微任务输出10,输出12
    async function async1() {
        console.log('async1 start');
        await async2();
        console.log('async1 end');
    }
    async function async2() {
        console.log('async2');
    }
    console.log('script start');
    setTimeout(function() {
        console.log('setTimeout');
    }, 0)
    async1();
    new Promise(function(resolve) {
        console.log('promise1');
        resolve();
    }).then(function() {
        console.log('promise2');
    });
    console.log('script end');
    
    1. 输出“script start”
    2. setTimeout宏任务
    3. 执行async1()输出async1 start,执行 async2(),输出 “async2”。
    4. async2执行完毕,将await async2 后面的代码加入到 微任务队列async1 end');
    5. 继续执行,new Promise, 同步输出“promise1”。promise.then,加入到微任务队列,
    6. 输出script end
    7. 当前宏任务执行完毕,查看微任务队列输出async1 end “promise2”
    8. 微任务全部执行完,检查宏任务,输出setTimeout

    改上面代码:

    async function async1() {
        console.log('async1 start');
        let p = await async2();
        console.log(p);
        console.log('async1 end');
    }
    async function async2() {
        console.log('async2');
        return new Promise((resolve, reject) => {
            resolve(10);
        })
    }
    console.log('script start');
    setTimeout(function() {
        console.log('setTimeout');
    }, 0)
    async1();
    new Promise(function(resolve) {
        console.log('promise1');
        resolve();
    }).then(function() {
        console.log('promise2');
    });
    console.log('script end');
    //第一次宏任务执行
    script start
    async1 start
    async2
    promise1
    script end
    //微任务执行
    promise2
    10
    async1 end
    //下一次宏任务执行
    setTimeout
    
    1. new promise的操作就跟你 new 一个普通函数没区别,所以这一句其实是宏任务,但后面的then是微任务
    2. resolved后的promise对象会在这该级别事件队列结束之后才开始执行,及执行与该轮微任务队列中,始于下一级别宏任务之前
    3. resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
    4. 如果出现两个这种微任务,则先出现的会先执行
    5. async 函数中,遇到 await 会跳出当前函数,并让出线程,再将await后面的代码放到 微任务(microtask)队列中

    起源地下载网 » 浏览器事件循环Event Loop

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元