js的运行机制
js的无数小坑坑
渐渐js用得慢慢多了些的时候,会发现这门语言有太多奇怪的东西,而这些东西总会引起一些好奇。特别我那个时候刚刚开始学习的时候,因为是第一次接触动态弱类型的语言,就感觉这鬼玩意好不严谨啊!特别还有这种操作…变量可以后声明,还不报错,这什么鬼!
console.log(a); //undefined
var a = 5;
那个时候还有这种
var a = 34;
setTimeout(function(){
// balabala
},1000)
console.log(a); //执行直接出现34,并没有等待1s
总觉得设定延时后,后面的内容会等待延时后执行(ps:忘了当时做的是什么了,貌似是个轮播图,这个就随意模拟一下当时的情况吧😰)
其实,众所周知等待了1s以后,当然只是执行了里面的回调函数,然而后面的打印a的确是在1s前面执行了…
还有就是上一篇js闭包(上)中提到的那个
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000)
console.info(i);
}
为了更加直观,这次咱在定时器外,循环内加一个打印i,运行后,瞬间打印出了1,2,3,4,5然后每隔一秒打印一个6打印5次。足以说明定时器是在循环外执行的!
我们在此可以看出,需要消耗时间的事件等待,io读写等耗时的任务,都会自动被移到程序最后执行。这就引发了好奇心,就想知道这是怎样的一个运行方式。
大家伙都明白,JavaScript是一门单线程语言。(虽然说用某些api等可以实现多线程,但单线程是它的本质)
传统的单线程会引发一些灾难性的问题,若是等待一个事件等消耗大量时间的内容,后面的内容会被阻塞,若是出现问题,后面的内容将永远不能得到执行权。
为了保证效率,js这门语言把执行任务在内核上就分为了,同步任务与异步任务。
执行机制
引用自阮一峰老师
同步任务在主线程上执行时候创建一个执行栈。
主线程之外,存在一个“任务队列”,只要异步任务有了结果,便在队列中放置一个事件。
一旦执行栈中的所有同步任务完成后,系统读取任务队列,查看里面的事件,那些异步任务结束等待,进入执行栈开始执行。
主线程不断重复上面三步。
异步任务就是消耗大量时间的io读写,以及事件等待,也就是说js的运行机制便是,先把能够快速执行的同步任务搞定并且读取消耗大量时间的任务,让其进入等待…待同步任务完成后,继续执行前面暂停的异步任务,主线程执行其异步任务,也就是执行其对应的回调函数。
也正是这种机制,高效的非阻塞io,让js在对抗io高并发场景体现出了足够的优势。
常用的异步
前面所提到的异步任务常用的大致有这些,发送或者接受网络请求req.send()
,定时器setTimeout()
和setInterval()
。
node中就更多了有磁盘读写,数据库读写,process.nextTick()
,setImmediate()
等…
示例
用这个来理解上次的运行机制我觉得OK!
其实,不用看程序,只看结果就能看出其实挺有意思的,和你平时能否合理利用时间差不多,毕竟人也不能一心二用,类似单线程。
用async和await模拟同步
const doSomeThing = (sth, time) => {
console.log(sth,`耗时 ${time} ms`);
return new Promise(resovle => {
setTimeout(resovle, time)
})
}
const Kalec = { doSomeThing };
const Karri = { doSomeThing };
;(async () => {
console.log('Karri来到门口想洗澡');
await Kalec.doSomeThing('我在蹲坑',3000);
await Karri.doSomeThing('Karri洗澡',3000);
console.log('Karri去做别的事情了');
})()
运行结果如下
Karri来到门口想洗澡
我在蹲坑 耗时3000ms
Karri等待时间过长有点生气,Karri洗澡 耗时3000ms
Karri去做别的事情了
异步为这样
const doSomeThing = (sth, time) => {
setTimeout(() => {
console.log(sth+'done!');
}, time);
}
const Kalec = { doSomeThing };
const Karri = { doSomeThing };
;(() => {
console.log('Karri来到门口想洗澡,发现我在蹲坑');
Kalec.doSomeThing('蹲坑',3000);
Karri.doSomeThing('Karri洗澡',3000);
console.log('Karri去做别的事情了');
})()
运行结果为
Karri来到门口想洗澡,发现我在蹲坑
Karri去做别的事情了
蹲坑done!
Karri洗澡done!