个人理解的JavaScript中的异步与同步
前言:很多东西,我会按照我理解白话的形式进行描述,便于理解和记忆,如果有描述错误的,还望指正
从开始学习JavaScript开始,无论是老师,还是书籍,都会告诉我们,JavaScript是单线程运行的,单线程意味着有多个任务必须“排队”,排好队后,再一个一个的执行,于是就有一个概念,任务队列。
同步
在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务
举个的栗子:你准备泡一壶茶,先洗了茶壶,然后在烧水,等待水开了后,再洗茶杯,再放入茶叶,然后将开水倒入杯中完成泡茶的动作,这里面的 洗茶壶——烧水——洗茶杯——放入茶叶——泡茶 这一系列的流程中,单一时间内按照顺序只做一件事,这就是同步
下面看段代码
function wait(){ const time = (new Date()).getTime();//先整个时间戳 while((new Date()).getTime() - time < 5000){} console.log('5秒过去了'); } wait(); console.log('慢死了');
上面代码中,wait是一个耗时较长的函数,后续的console.log只能等待这漫长的5秒钟后才能输出
异步
不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
举个栗子:你准备泡一壶茶,先洗了茶壶,然后在烧水,烧好开水是一个暂时得不到结果的事,就让他在那烧(挂起),我们可以利用烧水的时间洗茶杯和放入茶叶,然后等待水烧开,然后倒入茶杯,完成泡茶(这个栗子可能有些不准确,异步一定不能理解为一边....一边.....,比如一边玩着手机,一边吃着饭,同一时间做着两件事,这样的是双线程)
异步运行机制
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
来看一段代码
setTimeout(()=>{ console.log("我只延迟了一秒") },1000) setTimeout(()=>{ console.log("我只延迟了五秒") },5000) setTimeout(()=>{ console.log("我只延迟了零秒") },0) console.log("我不是setTimeout函数")
//我不是setTimeout函数 //我只延迟了零秒 //我只延迟了一秒 //我只延迟了五秒
上面代码中,console.log("我不是setTimeout函数")是主线程上的同步任务,根据异步的运行机制,优先清空主线程上的同步任务,所以优先打印了我不是setTimeout函数,然后在执行异步函数,依次打印我只延迟了零秒、我只延迟了一秒、我只延迟了五秒
我们再看一段代码
//做一个时间戳 var time = new Date().getTime() setTimeout(()=>{ console.log("我延迟3秒后显示"); console.log(`我实际的延迟时间:${new Date().getTime() - time}`) },3000) while(new Date().getTime() - time < 6000){} console.log("我6秒后的显示") //我6秒后的显示 //我延迟3秒后显示 //我实际的延迟时间:6002
上面代码中,while(new Date().getTime() - time < 6000){};console.log("我6秒后的显示")是主线程上的同步任务,所以优先运行,由于JavaScript是单线程的,所以本该在3秒打印:我延迟3秒后显示,在被同步任务阻塞后,被迫只能等待主线程上的同步任务完成后,再执行异步任务
那么异步函数之间会阻塞吗,我们来看一个例子
setTimeout(()=>{ console.log("我只延迟了一秒") },1000) setTimeout(()=>{ while(true){} },2000) setTimeout(()=>{ console.log("我只延迟了三秒") },3000) //我只延迟了一秒
上面代码中,当运行到第二个定时器时,由于定时器内while(true){}是一个死循环,导致浏览器假死,不能输出:我只延迟了三秒,由此可以看出,异步函数之间也会阻塞,也由此也可看出,JavaScript是单线程运行的
本文专业术语参考于: 阮大:JavaScript 运行机制详解:再谈Event Loop

浙公网安备 33010602011771号