非常推荐先看一下参考链接里两个 youtube 视频,分别介绍了 event loop 和 setTimeout 的实现原理,这对于理解 JavaScript 里异步操作实现非常有帮助。其次是关于 Promise object 的手动实现,有一些抽象,因为包含了大量的 callback 函数。这里一定要分清楚,哪些是函数的声明,哪些是函数的调用。
Event loop
JavaScript 是一种 single thread 的语言。既然单一线程,那么在某个时间点,只能完成一项任务。于是
$.get('http://twitter.com')
$.get('http://youtube.com')
$.get('http://google.com')
在单线程下,如果某一行执行所需要的时间太久了,那么余下的的 command 也无法执行,程序就在那一行停滞下来了。我们把这个叫做 blocking。然而事实上,当我们在使用 setTimeout()
函数时,程序似乎不会出现 blocking。
console.log('hi')
setTimeout(() => console.log('there'), 0)
console.log('Welcome!')
// output:
// hi
// Welcome
// there
这里哪怕是设置 delay 为 0s, setTimeout()
里的函数也是在最后才执行,是怎么回事呢?
在 JavaScript 的执行环境中,所有的需要运行的函数是单线程的,按照次序会出现在 call stack 里。而通常,JavaScript 要么在 Browser 里运行,要么在 Node 环境运行,在 Browser 里运行时,会有一整套来自 Browser 提供的 web API,同理在 node 环境里也有相应的 API。 setTimeout()
函数就来自这些提供的 API 中。当我们 declare and call 一个 setTimeout()
函数时,Browser 会生成一个 timer 计时器,计时器的时间达到时, setTimeout()
里定义的 callback 函数会进入到一个叫 task queue
的容器中,此时程序会去检测 call stack 是否为空,当 call stack 为空时,会将 task queue 中最上层的函数移入 call stack 中进行执行。因此本质上来讲,由于有 web API 的加持,最终类似 setTimeout()
函数还是使用了多线程。只不过对于 JavaScript 来讲,一直是执行的是 call stack 里的内容,可以认为一直是单线程操作。