第一题
setTimeout(() => console.log(0))
new Promise((resolve, reject) => {
console.log(1);
// resolve(999);
// reject('what')
})
// .then(d => {
// console.log(d);
// })
.then((d) => {
setTimeout((_) => console.log(2));
})
// .then()
.then((d) => {
console.log(3);
})
.catch((err) => console.log(err))
.finally((_) => console.log(4));
// 1 -> 3 -> 4 -> 0 -> 2
EventLoop事件循环
JS是单线程执行的,当遇到一个异步事件后,会先将这个事件挂起,继续执行栈中的其他任务。 当一个异步事件返回结果后,JS会将这个事件加入到另一个事件队列。被放入事件队列后不会立即执行它的回调,而是等待当前执行栈中的所有任务都执行完毕,主线程会去查找事件队列是否有任务。如果有,主线程会从里面取出排在第一位的事件,并将这个事件对应的回调放入执行栈中,然后执行其中的同步代码。 这种反复循环的过程就叫做事件循环。
异步任务队列里面又分为微任务和宏任务,并且微任务的优先级更高,微任务队列清空后,再去执行宏任务。
(一个宏任务要想结束,必然要去检查且清空微任务队列,才能开启下一轮事件循环(执行下一个宏任务),换句话说,一个宏任务即代表一个事件循环)
微任务包括 promise的回调、对DOM变化监听的 MutationObserver、process.nextTick(nodejs的)
宏任务包括 script脚本的执行 、 setTimeout 、setInterval 、setImmediate一类的定时事件、I/O操作、UI渲染、postMessage、MessageChannel。
题解:
setTimeout(() => console.log(0))宏任务,被挂起在宏任务队列- promise构造函数里面是同步代码,
console.log(1);会先执行; - 如果没有 resolve 或者 reject,promise后面的回调都不会执行,结果就是 1 -> 0;
- 反之,执行then回调,这个是微任务,被挂起在微任务队列,
console.log(3);和console.log(4);顺序执行; - then回调里遇到宏任务
setTimeout((_) => console.log(2));,被挂起在宏任务队列里,需要执行完所有微任务,才会执行。 - 最终结果:打印 1 -> 3 -> 4 -> 0 -> 2
promise
promise是异步编程的一种解决方案,它通常用来保存异步操作的结果。
promise可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
构建promise对象时,需要传入一个执行函数,resolve和reject两个函数作为参数,resolve被调用时,promise的状态会变成fulfilled(完成的),会触发then回调函数;reject被调用时,promise的状态会变成rejected(失败的),会触发catch回调函数。而且一旦状态改变,就不会再变,任何时候都可以得到这个结果。
promise的错误具有冒泡特性,会一直向后传递,可以通过最后一个 catch 来捕获异常,通过这种方式可以将所有 Promise 对象的错误合并到一个函数来处理,这样就解决了每个任务都需要单独处理异常的问题。
如何中断promise的链式调用:
- 抛出异常
throw xxx; - 通过reject中断
return Promise.reject('break with exception')
async/await的实现是基于 Promise的,async 函数就是返回Promise对象。
- 语法简洁,更像是同步代码,也更符合普通的阅读习惯;
- 改进JS中异步操作串行执行的代码组织方式,减少callback的嵌套;
- Promise中不能自定义使用try/catch进行错误捕获,但是在Async/await中可以像处理同步代码处理错误
- 缺点是如果多个异步代码没有依赖性却使用了 await 会导致性能上的降低。
Promise.all()
当参数数组中所有的 Promise 对象都变为resolve的时候,该方法才会返回。如果参数中的任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的 Promise 对象。
返回的结果是按照参数数组中的顺序来。(效果是“谁跑的慢,以谁为准执行回调”)
Promise.all([false, undefined, 0]);会返回[false, undefined, 0],原理是在promise内部使用了 Promise.resolve() 将这些参数值包裹了一下。
Promise.allSettled()和all类似,区别在于它可以拿到每个promise的状态,不管是否处理成功。
Promise.race()只要有一个promise对象进入FulFilled或者Rejected状态的话,就会继续进行后面的处理。(“谁跑的快,以谁为准执行回调”)
finally()在promise结束时,无论结果是fulfilled还是rejected,都会执行finally的回调函数,没有参数,表明与状态无关,通常可以用来关闭请求的loading。
第二题
var myObject = {
foo: 'bar',
func: function () {
var self = this;
console.log(this.foo); // bar
console.log(self.foo); // bar
(function () {
(
(self) => console.log(self.foo) // undefined
)(this);
console.log(self.foo); // bar
})();
}
}
myObject.func();
this
this是函数运行时自动生成的一个内部对象,指向调用它的对象。
- 作为普通函数执行时,this指向window,严格模式下this指向undefined。(
匿名函数的this指向window) - 函数作为对象里的方法被调用时,this指向该对象。
- 当用new的时候,this指向返回的这个对象。
- 箭头函数没有this绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined(箭头函数是定义时绑定,其他是允许时绑定)。
- 可以通过bind,apply,call方法显示改变this的指向。call和apply的区别,传参的格式不一样,call是字段分开,apply是数组。
- this的优先级:new > 显式 > 隐式 > 默认