在 JS 代码中,异步无处不在,比如 Ajax 通信,Node 中的文件读写等等。而只有搞清楚异步编程的原理和概念,才能在 JS 的世界中任意驰骋,随便撒欢。
单线程 JavaScript 异步方案
首先我们要了解,JavaScript 代码的运行是单线程。
采用单线程模式工作的原因也很简单,最早是为了在页面中实现 Dom 操作。如果采用多线程,就会造成复杂的线程同步问题。如果一个线程修改了某个元素,另一个线程又删除了这个元素,浏览器渲染就会出现问题。
单线程的含义就是,JS 执行环境中负责执行代码的线程只有一个。类似于只有一个人干活。他一次只能做一个任务,如果有多个任务自然是要排队的。
优点:安全,简单。
缺点:遇到任务量大的操作,会阻塞。后面的任务会长时间等待,出现假死的情况。
为了解决阻塞的问题,Javascript 将任务的执行模式分成了两种,同步模式( Synchronous)和 异步模式( Asynchronous)。
同步模式与异步模式
在程序中,代码依次执行,后面的任务需要等待前面任务执行结束后,才会执行。同步并不是同时执行,而是排队执行。
先来看一段代码:
console.log('global begin') function bar () { console.log('bar task') } function foo () { console.log('foo task') bar() } foo() console.log('global end')复制代码
用动画形式展现下同步代码的执行过程:
如图所示,代码会按照既定的语法规则,依次执行。如果中间遇到大量复杂任务,后面的代码则会阻塞等待。
再来看一段异步代码:
console.log('global begin') setTimeout(function timer1 () { console.log('timer1 invoke') }, 1800) setTimeout(function timer2 () { console.log('timer2 invoke') setTimeout(function inner () { console.log('inner invoke') }, 1000) }, 1000) console.log('global end')复制代码
异步代码的执行,要相对复杂一些。
代码首先按照同步模式执行,当遇到异步代码时,会开启异步执行线程。
在上面的代码中,setTimeout 会开启环境运行时的执行线程运行相关代码。代码运行结束后,会将结果放入到消息队列,等待 JS 线程结束后,消息队列的任务再依次执行。
流程图如下:
下篇,我们来聊聊回调函数和异步方案(ง •_•)ง。