javascript执行机制

对于javascript执行机制,大家应该并不陌生,很多面试题都会出现,比如:

  setTimeout(function () {
        console.log('定时器')
    });
    new Promise(function (resolve) {
        console.log('Promise');
        resolve();
    }).then(function () {
        console.log('Promise回调')
    });
    console.log('结束');
    //'Promise'
    //'结束'
    //'Promise回调'
    //'定时器'

上面这个面试题,如果不懂javascript执行机制的话估计很难做的出来!

一.javascript是一门单线程语言

js这门语言最大的特点就是单线程,即同一个时间只能做一件事情,做完一件事情才可以再做别的事情,好比很多人在银行排队取钱,你是窗口服务员,你每次只能为一个客户取钱,不能一次为多个客户取钱,那不乱套了吗?JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以一切javascript版的"多线程"都是用单线程模拟出来的,一切javascript多线程都是纸老虎!这个新标准并没有改变JavaScript单线程的本质。

二.javascript事件循环机制

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。好比之前说的排队取钱,如果一个客户取钱要花很多时间,后面的人只能一直等着,但是我们在浏览页面网站的时候,有些图片要通过数据请求后再加载才会显示,那么我们整个页面是不是要等某个比较大的图片没加载出来而整个网站都没显示出来呢,显然是不行的,所以JavaScript语言的设计者意识到,于是,所有任务可以分成两种

1.同步任务(synchronous)

同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务,所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。执行栈执行顺序是后进先出。

2.异步任务(asynchronous)

异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

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

三.宏任务和微任务的区别

macro-task(宏任务):包括整体代码script,setTimeout,setInterval
micro-task(微任务):Promise,process.nextTick
事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。在执行宏任务或者微任务过程中,里面的微任务优先级是大于宏任务的,也就是先执行微任务再执行宏任务并且任务队列的执行顺序是先进先出的(哪个宏任务/微任务先进去,先执行哪个宏任务/微任务)。
在这里插入图片描述
我们回来最开始的那段代码:

 setTimeout(function () {
        console.log('定时器')
    });
    new Promise(function (resolve) {
        console.log('Promise');
        resolve();
    }).then(function () {
        console.log('Promise回调')
    });
    console.log('结束');
    //'Promise'
    //'结束'
    //'Promise回调'
    //'定时器'

我们来分析下这段代码:
1.首先进去一个宏任务(整个js就是个宏任务)
2.然后一开始看到个setTimeout宏任务,进入Event Table并注册函数,然后Event Table会将这个函数移入任务队列(Event Queue)
3.接下来碰到个new Promise 执行同步代码 console.log(‘Promise’); 所以先输出’Promise’,
4.接下来碰到then回调微任务,进入Event Table并注册函数,然后Event Table会将这个函数移入任务队列(Event Queue)
5.碰到 console.log(‘结束’); 输出’结束’
6.判断有没有执行的微任务?有的,有then回调微任务,执行 console.log(‘Promise回调’) 输出’Promise回调’,
7.最后开始一轮新的事件循坏,先找到setTimeout宏任务,执行console.log(‘定时器’),输出 ‘定时器’,再找下有没有微任务?没有了,再开始开始一轮新的事件循坏,找有没有宏任务?也没有了,所以事件循坏结束

四.定时器(如果不是很了解的点击这里

定时器功能主要由setTimeout()和setInterval()这两个函数来完成,它们的内部运行机制完全一样,区别在于前者指定的代码是一次性执行,后者则为反复执行。以下主要讨论setTimeout(),setTimeout()接受两个参数,第一个是回调函数,第二个是推迟执行的毫秒数。

  console.log(1);
    setTimeout(function () {
        console.log(2);
    }, 1000);
    console.log(3);

上面代码的执行结果是1,3,2,因为setTimeout()将 console.log(2) 推迟到1000毫秒之后执行。

setTimeout(function(){console.log(1);}, 0);
console.log(2);

上面代码的执行结果总是2,1,因为只有在执行完 console.log(2) 以后会去执行"任务队列"中的回调函数。
setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。需要注意的是,setTimeout()只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证回调函数一定会在setTimeout()指定的时间执行。

   for (var i = 0; i < 5; i++) {
        setTimeout(function () {
            console.log(i);
        },i * 1000);
    };

看看这个代码输出什么,并且输出的时间间隔是多少?

下面我们来分析下,setTimeout有2个参数,第一个是个函数,第二个是延迟时间,i*1000很显然是上面的i的值,当i=0时候,相当过0秒把函数先Event Table然后再放进任务队列,然后i=1时,也是过1秒把函数先注册再放进任务队列,后面的也是一样,根据任务队列先进先出的顺序原则,由于主线程中没有其他任务,所以直接执行任务队列的函数,相当每隔1秒执行 console.log(i); 这里i由于异步的肯定是5了,所以每隔1秒输出一个5。

   for (var i = 0; i < 5; i++) {
        setTimeout((function (i) {
            console.log(i);
        })(i), i * 1000);
    };

改了下代码,里面的函数是个自执行函数,i当参数传了进去,结果又不一样了,因为一开始就会自执行,所以后面的延迟时间没用,输出结果几乎同时输出1,2,3,4。

好了,到这里javascript执行机制就差不多已经说完了(如果有不对之处,欢迎指正,不胜感激!!!),欢乐的时光总是过得特别快,又到时候和大家讲拜拜!!

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值