1.为何try里面放return,finally还会执行,理解其内部机制
总结:
- 不管有没有异常,finally中的代码都会执行
- 当try、catch中有return时,finally中的代码依然会继续执行
- finally是在return后面的表达式运算之后执行的,此时并没有返回运算之后的值,而是把值保存起来,不管finally对该值做任何的改变,返回的值都不会改变,依然返回保存起来的值。也就是说方法的返回值是在finally运算之前就确定了的。
- 如果return的数据是引用数据类型,而在finally中对该引用数据类型的属性值的改变起作用,try中的return语句返回的就是在finally中改变后的该属性的值。
- finally代码中最好不要包含return,程序会提前退出,也就是说返回的值不是try或catch中的值
-
先执行try中的语句,包括return后面的表达式,
有异常时,先执行catch中的语句,包括return后面的表达式,
然后执行finally中的语句,如果finally里面有return语句,会提前退出,
最后执行try中的return,有异常时执行catch中的return。 -
在执行try、catch中的return之前一定会执行finally中的代码(如果finally存在),如果finally中有return语句,就会直接执行finally中的return方法,所以finally中的return语句一定会被执行的。编译器把finally中的return语句标识为一个warning.
2.JavaScript如何实现异步编程,可以详细描述EventLoop机制
ES 6以前:
-
回调函数
-
事件监听(事件发布/订阅)
-
Promise对象
ES 6:
- Generator函数(协程coroutine)
ES 7:
- async和await
Event Loop 是什么
-
JavaScript的事件分两种,宏任务(macro-task)和微任务(micro-task)
宏任务:包括整体代码script,setTimeout,setInterval
微任务:Promise.then(非new Promise),process.nextTick(node中) -
事件的执行顺序,是先执行宏任务,然后执行微任务,这个是基础,任务可以有同步任务和异步任务,同步的进入主线程,异步的进入Event Table并注册函数,
-
异步事件完成后,会将回调函数放入Event Queue中(宏任务和微任务是不同的Event Queue),同步任务执行完成后,会从Event Queue中读取事件放入主线程执行,
-
回调函数中可能还会包含不同的任务,因此会循环执行上述操作。
3.宏任务和微任务分别有哪些
** JavaScript中分为同步任务和异步任务,而异步任务中又分为宏任务和微任务两种;**
- js代码在执行时,首先执行同步任务,当同步任务执行完毕后,开始执行异步任务,异步任务执行时,首先按照代码的层级,同层级先执行微任务其次执行宏任务,以此类推~
表头 | 宏任务(macrotask) | 微任务(microtask) |
---|---|---|
谁发起的 | 宿主(Node、浏览器) | JS引擎 |
具体事件 | 1. script (可以理解为外层同步代码)2. setTimeout/setInterval3. UI rendering/UI事件4. postMessage,MessageChannel5. setImmediate,I/O(Node.js) | 1. Promise2. MutaionObserver3. Object.observe(已废弃;Proxy 对象替代)4. process.nextTick(Node.js) |
谁先运行 | 先运行 | 后运行 |
会触发新一轮Tick吗 | 会 | 不会 |
5.使用Promise实现串行
下面
/**
* 创建promise
* @param {Number} value
*/
function makePromise (value) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(value);
}, Math.random() * 1000)
})
}
/**
* 打印结果
* @param {Number} value
*/
function print (value) {
return value
}
let promises = [1, 3, 4, 5, 6].map((item, index) => {
return makePromise(item)
});
// 并行执行
Promise.all(promises)
.then(() => {
console.log('done')
})
.catch(() => {
console.log('error')
})
// 串行执行
let parallelPromises = promises.reduce(
(total, currentValue) => total.then(() => currentValue.then(print)),Promise.resolve()
)
parallelPromises
.then(() => {
// console.log('done')
})
.catch(() => {
console.log('done')
})
// 顺带复习一下reduce方法
reduce((total, currentValue, currentIndex, arr) => {}, initialValue)
let arr1 = [1, 2, 3, 4, 5]
let res = arr1.reduce((total, currentValue, currentIndex, arr) => {
return total + currentValue
});
6.Node与浏览器EventLoop的差异
浏览器:
宏任务单个执行:每执行一个后,就会去执行掉微任务队列中的所有任务。
UI渲染:每次一个循环结束后(一个宏+整列微任务),如果需要会执行UI渲染。
实时添加:微任务执行过程中可以向本队列继续添加微任务并顺序执行。
Node:
队列执行:任务执行时,会执行队列中的所有任务(包括宏任务队列)
nextTick优先:微任务分的更细,node将nextTick回调单独化为一组微任务,所有nextTick队列可以和微任务队列一样实时添加,而且整个队列始终先于微任务队列执行。