个人理解的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

posted @ 2019-10-23 16:54  小烂  阅读(195)  评论(0)    收藏  举报