前言
本文2925字,阅读大约需要10分钟。
总括: 本文梳理了异步代码和同步代码执行的区别,Javascript的事件循环,任务队列微任务队列等概念。
- 原文地址:Understanding Asynchronous JavaScript
- 公众号:「前端进阶学习」,回复「666」,获取一揽子前端技术书籍
未曾失败的人恐怕也未曾成功过。
Javascript是单线程的编程语言,单线程就是说同一时间只能干一件事。放到编程语言上来说,就是说Javascript引擎(执行Javascript代码的虚拟机)同一时间只能执行一条语句。
单线程语言的好处是你只管写不用担心并发问题。但这也意味着无法在不阻塞主线程的情况下去执行一些诸如网络请求的长时间操作。
设想下如果我们从某个接口请求一些数据,然后服务器需要一些时间才能将数据返回,此时就会阻塞主线程页面处于无响应的状态。
这里就是Javascript异步的用武之地了,我们可以通过异步操作(比如回调函数,promise和async/await)来执行长时间的网络请求而不阻塞主线程。
虽然说了解这些所有的概念不一定让你立刻成为一名出色的Javascript开发者,但了解异步会对你很有帮助。
话不多说,正文开始:)
同步的代码是怎么执行的
在深入研究Javascript的异步之前,我们先来看下同步的代码是如何在Javascript引擎中执行的。看例子:
const second = () => {
console.log('Hello there!');
}
const first = () => {
console.log('Hi there!');
second();
console.log('The End');
}
first();
要想理解上面的代码是如何在Javascript引擎中被执行的,我们必须要去理解Javascript的执行上下文和执行栈。
执行上下文
所谓的执行上下文是Javascript代码执行环境中的一个抽象的概念。Javascript任何代码都是在执行上下文中执行的。
函数内部的代码会在函数执行上下文中执行,全局的代码会在全局执行上下文中执行,每一个函数都有自己的执行上下文。
执行栈
顾名思义执行栈是一种后进先出(LIFO)的栈结构,它用来存储在代码执行阶段创建的所有的执行上下文。
基于单线程的原因,Javascript只有一个执行栈,因为是基于栈结构所以只能从栈的顶层添加或是删除执行上下文。
让我们回到上面的代码,尝试理解Javascript引擎是如何去执行它们的。
const second = () => {
console.log('Hello there!');
}
const first = () => {
console.log('Hi there!');
second();
console.log('The End');
}
first();
所以这里发生了什么呢?
当代码被执行时,首先一个全局执行上下文(这里用main()
表示)被创建然后压到执行栈的顶端。当执行到first()
这一行代码,它的执行上下文被压到执行栈的顶端。
紧接着,console.log('Hi there!');
的函数执行上下文被压到执行栈的顶端,执行结束后该执行上下文从执行栈弹出。然后调用second()
函数,该函数的执行上下文被压到执行栈的顶端。
然后执行console.log('Hello there!');
,对应的函数执行上下文被压入执行栈,执行结束被弹出,然后second()
函数执行结束,执行上下文被弹出。
console.log(‘The End’)
执行,函数执行上下文被压入执行栈,执行结束被弹出,此时first()
函数执行结束,对应执行上下文被弹出。
整个程序执行结束,