Open logan70 opened 4 years ago
JavaScript是单线程语言,也就是说同一时间只能运行一个任务。一般来说这没什么问题,但是如果运行耗时过长的任务,将会阻塞后续任务的执行,包括UI渲染。
所以一些非密集计算的任务,比如文件I/O,HTTP Request,定时器等任务,完全没必要在主线程等待其完成,而是应该在创建任务后交出主线程控制权,去执行其他任务,待其完成后再处理。这就引出了异步编程。
需要注意的是,ECMAScript(JavsScript的语言规范)并没有定义这些异步特性,所以异步特性的实现都需要依赖于JavaScript运行环境,例如浏览器、Node等。
浏览器提供了JS引擎不具备的特性,我们称之为Web API,例如我们常见的DOM事件监听、Ajax、定时器等。通过这些特性可以实现异步、非阻塞的行为。其执行机制会在下个部分 Event Loop 中详细讲解。
Web API
JavaScript发展至今,异步编程语法主要有以下几种:
/* ------------------- Callback ---------------- */ function asyncFn(callback) { setTimeout(() => { callback() }, 1000) } const callbackFn = () => console.log('Callback has been invoked') asyncFn(callbackFn) /* ------------------- Promise ---------------- */ function asyncFn() { return new Promise(resolve => { setTimeout(() => resolve(), 1000) }) } asyncFn().then(name => console.log('Promise fulfilled') /* ------------------- Generator ---------------- */ // Generator生成器函数执行时会返回一个Generator迭代器 // 也就是说Generator本身只是一个状态机,需要由调用者来改变它的状态 function *fetchUser () { const user = yield ajax() console.log(user) } const f = fetchUser() // 加入的控制代码 const result = f.next() result.value.then((d) => { f.next(d) }) /* ------------------- Async/Await ---------------- */ // Async/Await 可以理解为是 Generator + Promise 的语法糖 // async 对应 *,await 对应 yield,然后自动实现Generator的流程控制 async function getUser() { const user = await ajax() return user } // getUser用Generator + Promise表示并执行 function *getUser() { const user = yeild ajax() return user } const g = getUser() const result = g.next() result.value.then(res => { g.next(res) })
首先推荐一个Event Loop可视化执行的网站。
Script标签中的直接运行代码
Promise
process.nextTick
MutaionObserver
queueMicrotask
定时器
xhr
DOM Event
需要注意的是,若微任务执行过程中往微任务队列加入了新的微任务,也会在本步骤内被执行。
// 题目出自https://juejin.im/post/5a6155126fb9a01cb64edb45#heading-2 console.log(1) setTimeout(() => { console.log(2) new Promise(resolve => { console.log(3) resolve() }).then(() => { console.log(4) }) }) new Promise(resolve => { console.log(5) resolve() }).then(() => { console.log(6) })
1
5
6
2
3
4
1 5 6 2 3 4
理解了JS的执行机制,碰到类似题目就可以轻松应对:
// 题目出自https://juejin.im/post/5a6155126fb9a01cb64edb45 console.log(1) setTimeout(() => { console.log(2) new Promise(resolve => { console.log(3) resolve() }).then(() => { console.log(4) }) }) new Promise(resolve => { console.log(5) resolve() }).then(() => { console.log(6) }).then(() => { console.log(7) }) new Promise(resolve => { console.log(8) resolve() }).then(() => { console.log(9) }).then(() => { console.log(10) })
答案为1 5 8 6 9 7 10 2 3 4。
1 5 8 6 9 7 10 2 3 4
JavaScript异步编程及Event Loop
异步编程
为何需要异步编程
JavaScript是单线程语言,也就是说同一时间只能运行一个任务。一般来说这没什么问题,但是如果运行耗时过长的任务,将会阻塞后续任务的执行,包括UI渲染。
所以一些非密集计算的任务,比如文件I/O,HTTP Request,定时器等任务,完全没必要在主线程等待其完成,而是应该在创建任务后交出主线程控制权,去执行其他任务,待其完成后再处理。这就引出了异步编程。
需要注意的是,ECMAScript(JavsScript的语言规范)并没有定义这些异步特性,所以异步特性的实现都需要依赖于JavaScript运行环境,例如浏览器、Node等。
如何实现异步编程
浏览器提供了JS引擎不具备的特性,我们称之为
Web API
,例如我们常见的DOM事件监听、Ajax、定时器等。通过这些特性可以实现异步、非阻塞的行为。其执行机制会在下个部分 Event Loop 中详细讲解。异步编程语法
JavaScript发展至今,异步编程语法主要有以下几种:
Event Loop
首先推荐一个Event Loop可视化执行的网站。
基本概念
Script标签中的直接运行代码
、Web API
加入任务队列的回调函数。Promise
、process.nextTick
、MutaionObserver
、queueMicrotask
的回调函数。定时器
、xhr
、DOM Event
等,提供了处理异步任务的能力,异步任务完成后,将回调函数放入对应的任务队列(即微任务会放入微任务队列)。执行机制
Web API
处理(异步任务完成后Web API
将回调函数加入相应任务队列/微任务队列);巩固练习
1
;5
,然后该Promise变为fulfilled状态,将then内回调加入微任务队列;6
;2
,同步执行new Promise中函数,打印3
,Promise置为fulfilled,将then内回调加入为任务队列;4
;1 5 6 2 3 4
。加强练习
理解了JS的执行机制,碰到类似题目就可以轻松应对:
答案为
1 5 8 6 9 7 10 2 3 4
。