Javascript同步vs异步

545 阅读3分钟

概念

在编写Js程序的时候,毫无疑问的就会遇到发送http请求、读取数据库数据、用户点击按钮... 它们都有一个相同点就是都是耗时操作,也有不耗时操作,它们都在等待着被执行。

只有当当前任务完成,才回去继续执行下一个任务,这也是程序能够正常运行的根本所在,如果所有的任务都在同一时刻一起执行,那么处理器到底应该先执行哪一个呢?况且要执行的任务之间大多还存在着某种不可描述的关系,比如后面一个任务需要先获取到前面一个任务的执行结果才能够继续执行,否则就可能直接报错了。

同步

比如一百个人在排队上厕所,但是厕所只有两个坑位,这时候人们又不愿意去找别的WC,那么只能够等前面的人出来后面的人进去,这样循环往复。

同步要求多个任务必须排队等待被处理器执行,只有等前一个任务执行完成了,后面的任务才能够开始执行,也就是必须按照先后顺序来。

异步

还是一百个人上厕所,当快到你们的时候你前面俩个大哥在旁边抽着烟没注意到自己了,这个时候你就先上去了,可能等你出来了他们也还在抽烟...

异步要求有多个任务需要被执行时,谁快就先执行谁,可以不按照顺序来。

同步

Javascript是单线程语言,浏览器中负责解释和执行Javascript代码的只是一个线程(单线程),也就是说同一时刻,同一个Javascript引擎只能够执行一个任务。在程序运行的时候,所有的任务都在Javascript的主线程上排队执行。当一个任务被执行的时候,它就会被推入调用栈,执行完毕后,又被推出调用栈。

想要理解什么是主线程,我们先要搞懂什么是执行环境调用栈

  • 执行上下文 执行上下文指的是执行JavaScript代码的环境,它是一个抽象的概念,就像我们每个人都活在时间的长河中,但是时间却看不见摸不着。 只要有代码在JavaScript中运行,它就一定是在执行上下文中运行。函数的代码在函数的执行上下文内执行,而全局环境中的代码则在全局执行上下文内执行,而且每个函数都会创建属于自己的执行上下文

  • 调用栈 调用栈顾名思义是一个栈,是一种数据结构。它的作用就是存储代码运行时的执行上下文。

Javascript跟C语言类似,代码都是一行一行执行的。当上面的代码开始执行之前,Javascript引擎会先创建调用栈,然后创建一个全局执行上下文(main主线程),接着代码开始执行。

异步

function f1(){
    console.log(1)
}

function f2() {
  setTimeout(() => {
    console.log(2)
  }, 2000)
}

function f3(){
    console.log(3)
}


f1()
f2()
f3()

// 1, 3, 2

这里因为f2是一个耗时操作,需要耗费2s才能够运行完毕,所以这就触发了Javascript的异步执行机制。

回调函数 如果我们就是不想输出132而是123呢,其实可以通过回调函数来解决这个问题

function f1(){
    console.log(1)
}

function f2(callback) {
  setTimeout(() => {
    console.log(2)
    
    // 这就是 f3()
    callback()
  }, 2000)
}

function f3(){
    console.log(3)
}

f1()
f2(f3)

将f3作为参数传入f2,f2在执行完耗时操作之后,再来调用我们传入的f3,这样就保证了我们所要求的代码执行顺序, 回调函数是Javascript中的一种异步编程方式。