学不动了怎么办?换个姿态持续学。
JS引擎
js引擎执行过程分为三个阶段:
- 解释阶段
js代码加载结束后进入此阶段,此阶段实现由字符串到机器码的转变过程。
- 预处理(编译)阶段与执行阶段
因为js是解释性语言,所以会一边编译一边执行。创立变量对象产生在预编译阶段(编译阶段生成执行上下文,执行上下文蕴含变量,作用域链和this),变量赋值产生在执行阶段。
同步VS异步
浏览器内核是多线程的,和js执行相干的线程蕴含:
- js引擎主线程,负责解释和执行js代码。
- ajax申请线程。
- DOM解决线程。
- 定时工作线程。
所有同步工作都是在js引擎主线程上依照程序一次执行,造成一个执行栈,工作的执行也是工作在执行栈上的一直入栈和出栈。除了执行栈,事件触发线程治理一个工作队列,异步工作有了后果,会向工作队列放入回调。一旦所有同步工作执行结束之后(js引擎闲暇),会读取工作队列,将工作放入执行栈并执行。
**事件循环**(Event Loop)指的是脚本运行时,先顺次运行执行栈,而后从工作队列中取出事件执行来运行工作队列中的工作,这个过程是一直反复的。
异步编程
回调函数
回调函数是js异步编程的罕用形式,然而回调函数容易层层嵌套造成回调天堂。
const getData = (data, callback) => { setTimeout(() => { callback(data + 1) }, 1000)}getData(1, function (v1) { getData(v1, function (v2) { getData(v2) })})
层层嵌套的代码,不仅影响代码逻辑,而且可读性十分差,能够用Promise 对象、Generator 函数、async 函数代替,使得回调扁平化。
Promise
Promise对象提供then办法,then办法能够链式调用,链式调用时then办法顺次执行。
const getData = function (data) { return new Promise(resolve => { setTimeout(() => { resolve(data + 1) }, 1000) })}getData(1) .then(v1 => { return getData(v1) }) .then(v2 => { return getData(v2) })
Promise对象不仅能解决回调天堂问题,同时还提供了动态的all和race办法。all办法接管一个数组,当数组中存在异步调用的时候,只有异步办法全副执行实现之后,then办法才执行。race办法同样承受一个数组,与all不同的是,参数数组中的多个异步调用,如果其中一个实现或者失败,那么then办法就会执行,能够用race办法实现ajax超时判断。
const getData = function (data) { return new Promise(resolve => { setTimeout(() => { resolve(data + 1) }, 1000) })}function timeout(duration) { return Promise.race([ getData(1), new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('调用超时')) }, duration) }) ])}timeout(500).then(value => { console.log(value)}).catch(err => { console.log(err.message)})
因为设置的超时工夫是500毫秒,此时getData异步函数尚未执行实现,因而race办法会捕捉到超时谬误。
Generator
Generator生成器,能够利用*和yield关键字解决异步嵌套的回调天堂问题。
const getData = function (data) { return new Promise(resolve => { setTimeout(() => { resolve(data + 1) }, 1000) })}function* gen() { let v1 = yield getData(1) console.log(v1) // yield会使函数暂停执行,当调用next办法时会继续执行,同时next办法传入的参数会作为yield的返回值 let v2 = yield getData(v1) console.log(v2)}function co(generator) { let g = generator() function handleResult(result) { if (result.done) return else { result.value.then((value) => { handleResult(g.next(value)) }, err => { console.log(err) }) } } handleResult(g.next())}co(gen)
async
通常async和await一起应用,是一种语法糖,能够了解为通过async和await关键字能够更不便的实现上节中的generator。
const getData = function (data) { return new Promise(resolve => { setTimeout(() => { resolve(data + 1) }, 1000) })}async function main() { let v1 = await getData(1) console.log(v1) // yield会使函数暂停执行,当调用next办法时会继续执行,同时next办法传入的参数会作为yield的返回值 let v2 = await getData(v1) console.log(v2)}main()
微工作vs宏工作
如果执行上面的代码:
setTimeout(() => { console.log('timeout')}, 0)new Promise(resolve => { resolve('promise')}).then(v => { console.log(v)})
会发现,同样是异步操作,Promise的回调函数会早于setTimeout的回调函数,这就波及到微工作(Promise等)和宏工作(setTimeout等)。
宏工作和微工作均为异步工作,只是执行程序不同,通常状况下,会优先执行微工作,当微工作执行结束之后,再执行宏工作。
盗用网上的执行逻辑图:
常见的宏工作有:setTimeout(等同延迟时间下,setTimeOut的优先级高于setImmediate),setInterval, setImmediate, I/O, UI rendering。
常见的微工作有:Promise,process.nextTick(在node环境中nextTick优先级高于promise)。
参考:
js运行机制及异步编程(一)
js运行机制及异步编程(二)