微任务与宏任务
- 宏任务
- 为了协调任务在主线程上执行,页面进程引入消息队列和事件循环机制,渲染进程内部会维护多个消息队列,主线程从这些任务队列中取出任务执行,这写消息队列种的任务称为宏任务。
- 微任务
- 第一种:把异步回调函数封装成一个宏任务,添加到消息队列尾部,当循环系统执行到该任务的时候执行回调函数。
- 第二种:在主函数执行结束后,当前宏任务结束之前执行回调函数,这通常都是以微任务形式体现。
- 本质是:微任务就是一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前。
- 每个宏任务都关联了一个微任务队列。
- 微任务的产生,MutationObserver监控某个DOM节点,然后再通过JS来修改。当DOM节点发生变化时,就会产生DOM变化记录的微任务;使用Promise时也会产生微任务。
- 微任务执行过程中产生新的微任务不会推迟到下个宏任务中执行,而是在当前的宏任务中继续执行。
- 通过异步操作解决了同步操作的性能问题,通过微任务解决了实时性的问题。
Promise的使用
- Promise 通过回调函数延迟绑定和回调函数返回值穿透的技术,解决了循环嵌套。
- Promise对象的错误具有冒泡性质,会一直象后传递,直到被onReject函数处理或catch语句捕获为止。
- Promise的onResolve需要延时调用,因此和微任务挂钩。
- 回调地狱:下一任务依赖上个任务的结果,并在上个任务的回调函数内部执行新的业务逻辑,这样当嵌套层次过多后,代码的可读性变差。
- Promise回调函数的延时绑定:先创建Promise对象,通过Promise的构造函数executor来执行业务逻辑,创建好Promise对象之后,在使用 then()来设置回调函数。
- Promise 将回调函数 onResolve的返回值穿透至最外层:Promise执行结果会保存在promise的data变量中,然后是.then方法返回值为使用resolve或rejected回调方法新建的一个promise对象。即例如成功则返回new Promise(resolved),将前一个promise的data值赋值给新建的promise。
- Promise内部有resolved_和rejected_变量保存成功和失败的回调,进入.then(resolved,rejected)时会判断rejected参数是否为函数,若是函数,错误时使用rejetcted处理错误;若不是,则错误是直接throw错误,一直传递到最后的捕获,若最后没有捕获,则会报错。
async/await:使用同步的方式去写异步代码
- 生成器(Generator)是如何工作的?
- 生成器函数是一个带星号函数,而且是可以暂停执行和恢复执行的。
- 在生成器函数内部执行一段代码,如果遇到yield关键字,那么js引擎将返回关键字后面的内容给外部,并暂停该函数的执行。
- 外部函数可以通过next方法恢复函数的执行。
- 协程:是一种比线程更加轻量级的存在。你可以把协程看成是跑在线程上的任务,一个线程上可以存在多个协程,但是在线程上同时只能执行一个协程。
- 协程不是被操作系统内核所管理,而完全由程序所控制,线程间的切换不会消耗太多资源。通过yield关键字交出主线程控制权同时js引擎会保存当前协程的调用栈信息,并切换到父协程的调用栈,通过next()时JS引擎会保存当前的调用栈信息,并将切换到协程调用栈,通过returen关键字关闭协程。
- 生成器就是协程的一种实现方式,协程和Promise相互配合执行的一个大致流程,把执行生成器的代码封装成一个函数,并把这个执行生成器代码的函数称为执行器(co框架)
- async/await
- async/await技术背后的秘密就是Promise和生成器应用,往底层就是微任务和协程应用
- async是一个通过异步执行并隐式返回Promise作为结果的函数。
- await 默认创建一个Promise对象,然后通过微任务执行,最后通过Promise回调函数将值透传至最外层。
async function foo() {
console.log('foo')
}
async function bar() {
console.log('bar start')
await foo()
console.log('bar end')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout')
}, 0)
bar();
new Promise(function (resolve) {
console.log('promise executor')
resolve();
}).then(function () {
console.log('promise then')
})
console.log('script end')
1. 首先在主协程中初始化异步函数foo和bar,碰到console.log打印script start;
2. 解析到setTimeout,初始化一个Timer,创建一个新的task
3. 执行bar函数,将控制权交给协程,输出bar start,碰到await,执行foo,输出foo,创建一个 Promise返回给主协程
4. 将返回的promise添加到微任务队列,向下执行 new Promise,输出 promise executor,返回resolve 添加到微任务队列
5. 输出script end
6. 当前task结束之前检查微任务队列,执行第一个微任务,将控制器交给协程输出bar end
7. 执行第二个微任务 输出 promise then
8. 当前任务执行完毕进入下一个任务,输出setTimeout