JavaScript 高级程序设计
原型链与面向对象编程
this 指向、闭包和作用域
Promise规范及应用
手写JS 常见 API
本文档使用 MrDoc 发布
-
+
首页
Promise规范及应用
## PromiseA+ 规范 ### 术语 1. promise 是一个有 then 方法的对象或者是函数,行为遵循本规范 2. thenable 是一个有 then 方法的对象或是函数 3. value 是 promise 状态成功时的值,也就是 resolve 的参数, 包括各种数据类型,也包括 undefined、thenable 或者是 promise 4. reason 是 promise 状态失败时的值,也就是 reject 的参数,表示拒绝的原因 5. exception 是一个使用 throw 抛出的异常值 ### 规范 以下分为几部分来讲解 PromiseA+ 规范 #### Promise States promise 应该有三种状态。要注意他们的流转关系。 1. pending * 1.1 初始化状态,可改变 * 1.2 一个 promise 在 resolve 或者 reject 前都处于这个状态 * 1.3 可以通过 resolve -> fulfilled 状态 * 1.4 可以通过 reject -> rejected 状态 2. fufilled * 2.1 最终态,不可改变 * 2.2 一个 promise 被 resole 后会变成这个状态 * 2.3 必须拥有一个 value 值 3. rejected * 3.1 最终态,不可改变 * 3.2 一个 promise 被 reject 后会变成这个状态 * 3.3 必须拥有一个 reason Tips: 总结下来,就是 promise 的状态流转 pending -> resolve(value) -> fulfilled pending -> reject(reason) -> rejected #### then promise 应该提供一个 then 方法,用来访问最终的结果,无论是 value 还是 reason ```js promise.then(onFulfilled, onRejected) ``` 1. 参数要求 * 1.1 onFulfilled 必须是函数类型,如果不是函数,应该被忽略. * 1.2 onRejected 必须是函数类型,如果不是函数,应该被忽略 2. onFulfilled 特性 * 2.1 在 promise 变成 fulfilled 时,应该调用 onFulfilled,参数是 value * 2.2 在 promise 变成 fulfilled 之前,不应该被调用 * 2.3 只能被调用一次(所以在实现的时候需要用一个变量来限制执行次数) 3. onRejected 特性 * 3.1 在 promise 变成 rejected 时,应该调用 onRejected,参数是 reason * 3.2 在 promise 变成 rejected 之前,不应该被调用 * 3.3 只能被调用一次(所以实现的时候需要一个变量来限制次数) 4. onFulfilled 和 onRejected 应该是微任务(实现时可以使用 queueMicrotask 模拟) 5. then 方法可以被调用多次 * 5.1 promise 状态变成 fulfilled 后,所有的 onFulfilled 回调都需要按照 then 的顺序执行,也就是按照注册顺序执行(所以在实现的时候需要用一个数组来存放多个 onFulfilled 的回调) * 5.2 promise 状态变成 rejected 后,所有的 onRejected 回调都需要按照 then 的顺序执行,也就是按照注册顺序执行(所以在实现的时候需要用一个数组来存放多 onRejected 的回调) 6. 返回值 then 应该返回一个 promise ```js promise2 = promise1.then(onFulfilled,onRejected); ``` * 6.1 onFulfilled 或 onRejected 执行的结果为 x,调用 **resolvePromise** * 6.2 如果 onFulfilled 或者 onRejected 执行时**抛出异常** e, promise2 **需要被 reject** * 6.3 如果 onFulfilled 不是一个函数,promise2 以 promise1 的 value 触发 fulfilled * 6.4 如果 onRejected 不是一个函数,promise2 以 promise1 的 reason 触发 rejected 7. resolvePromise 该处最难理解,具体实现时,一一对照 ```js resolvePromise(promise2,x,resolve,reject) ``` * 7.1 如果 promise2 和 x 相等,那么 reject TypeError * 7.2 如果 x 是一个 promise 如果 x 是一个 pengding 态,那么 promise 必须要在 pending,知道 x 变成 fulfilled or rejected. 如果 x 被 fulfilled,fulfill promise with the same value. 如果 x 被 rejected,reject promise with the same reason * 7.3 如果 x 是一个 object 或者是一个 function let then = x.then 如果 x.then 出错,那么 reject promise with e as the reason. 如果 then 是一个函数,then.call(x,resolvePromiseFn,rejectPromise) resolvePromiseFn 的入参是 y,执行 resolvePromise(promise2,y,resolve,reject); rejectPromise 的 入参是 r, reject promise with r. 如果 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。 如果调用 then 抛出异常 e 如果 resolvePromise 或 rejectPromise 已经被调用,那么忽略 则,reject promise with e as the reason 如果 then 不是一个 function. fulfill promise with x. ## 用 class 实现一个 promise 1. 通常 new Promise()来实现 ```js class MPromise { constructor() { } } ``` 2. 定义三种状态类型 ```js const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; ``` 3. 设置初始状态 ```js class MPromise { constructor() { // 初始状态为 pending this.status = PENDING; this.value = null; this.reason = null; } } ``` 4. resolve 和 reject 方法 1. 根据刚才的规范, 这两个方法是要更改 status 的, 从 pending 改到 fulfilled/rejected. 2. 注意两个函数的入参分别是 value 和 reason. ```js class MPromise { constructor() { // 初始状态为 pending this.status = PENDING; this.value = null; this.reason = null; } resolve(value) { if (this.status === PENDING) { this.value = value; this.status = FULFILLED; } } reject(reason) { if (this.status === PENDING) { this.reason = reason; this.status = REJECTED; } } } ``` 5. 是不是发现咱们的 promise 少了入参, 咱们来加一下 1. 入参是一个函数, 函数接收 resolve 和 reject 两个参数. 2. 注意在初始化 promise 的时候, 就要执行这个函数, 并且有任何报错都要通过 reject 抛出去 ```js class MPromise { constructor(fn) { // 初始状态为 pending this.status = PENDING; this.value = null; this.reason = null; try { // 函数接收 resolve 和 reject 两个参数. fn(this.resolve.bind(this), this.reject.bind(this)); } catch (e) { // 异常报错,由 reject 抛出 this.reject(e); } } resolve(value) { if (this.status === PENDING) { this.value = value; this.status = FULFILLED; } } reject(reason) { if (this.status === PENDING) { this.reason = reason; this.status = REJECTED; } } } ``` 6. 接下来来实现一下关键的 then 方法 1. then 接收两个参数, onFulfilled 和 onRejected ```js then(onFulfilled, onRejected) {} ``` 2. 检查并处理参数, 之前提到的如果不是 function, 就忽略. 这个 **忽略指的是原样返回 value 或者 reason** . ```js isFunction(param) { return typeof param === 'function'; } then(onFulfilled, onRejected) { const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value } const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; } ``` 3. 要知道.then 的返回值整体是一个 promise, 所以咱们先用 promise 来包裹一下, 其他逻辑待会再实现. ```js then(onFulfilled, onRejected) { const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value } const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; const promise2 = new MPromise((resolve, reject) => {}) return promise2 } ``` 4. 根据当前 promise 的状态, 调用不同的函数 ```js then(onFulfilled, onRejected) { const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value } const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; const promise2 = new MPromise((resolve, reject) => { switch (this.status) { case FULFILLED: { realOnFulfilled() break; } case REJECTED: { realOnRejected() break; } } }) return promise2 } ``` 5. 这个时候有的同学要问了, 你这样写, 是在 then 函数被调用的瞬间就会执行. 那这时候如果 status 还没变成 fulfilled 或者 rejected 怎么办, 很有可能还是 pending 的. 所以我们需要一个状态的监听机制, 当状态变成 fulfilled 或者 rejected 后, 再去执行 callback. 1. 那么我们首先要拿到所有的 callback, 然后才能在某个时机去执行他. 新建两个数组, 来分别存储成功和失败的回调, 调用 then 的时候, 如果还是 pending 就存入数组. ```js FULFILLED_CALLBACK_LIST = []; REJECTED_CALLBACK_LIST = []; then(onFulfilled, onRejected) { const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value } const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; const promise2 = new MPromise((resolve, reject) => { switch (this.status) { case FULFILLED: { realOnFulfilled() break; } case REJECTED: { realOnRejected() break; } case PENDING: { this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled) this.REJECTED_CALLBACK_LIST.push(realOnRejected) } } }) return promise2 } ``` 2. 在 status 发生变化的时候, 就执行所有的回调. 这里咱们用一下 es6 的 getter 和 setter. 这样更符合语义, 当 status 改变时, 去做什么事情. (当然也可以顺序执行, 在给 status 赋值后, 下面再加一行 forEach) ```js _status = PENDING; get status() { return this._status; } set status(newStatus) { this._status = newStatus; switch (newStatus) { case FULFILLED: { this.FULFILLED_CALLBACK_LIST.forEach(callback => { callback(this.value); }); break; } case REJECTED: { this.REJECTED_CALLBACK_LIST.forEach(callback => { callback(this.reason); }); break; } } } ``` 7. then 的返回值 上面只是简单说了下, then 的返回值是一个 Promise, 那么接下来具体讲一下返回 promise 的 value 和 reason 是什么. 1. 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e。(这样的话, 我们就需要手动 catch 代码,遇到报错就 reject) ```js then(onFulfilled, onRejected) { const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value } const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; const promise2 = new MPromise((resolve, reject) => { const fulfilledMicrotask = () => { try { realOnFulfilled(this.value); } catch (e) { reject(e) } }; const rejectedMicrotask = () => { try { realOnRejected(this.reason); } catch (e) { reject(e); } } switch (this.status) { case FULFILLED: { fulfilledMicrotask() break; } case REJECTED: { rejectedMicrotask() break; } case PENDING: { this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask) this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask) } } }) return promise2 } ``` 7.2 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值 7.3 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因。 **需要注意的是,如果 promise1 的 onRejected 执行成功了,promise2 应该被 resolve** 这里咱们其实已经在参数检查的时候做过了, 也就是这段代码 ```js const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value } const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; ``` 7.4 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行 resolvePromise 方法 ```js then(onFulfilled, onRejected) { const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value } const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; const promise2 = new MPromise((resolve, reject) => { const fulfilledMicrotask = () => { try { const x = realOnFulfilled(this.value); this.resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }; const rejectedMicrotask = () => { try { const x = realOnRejected(this.reason); this.resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } } switch (this.status) { case FULFILLED: { fulfilledMicrotask() break; } case REJECTED: { rejectedMicrotask() break; } case PENDING: { this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask) this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask) } } }) return promise2 } ``` 8. resolvePromise ```js resolvePromise(promise2, x, resolve, reject) { // 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise // 这是为了防止死循环 if (promise2 === x) { return reject(new TypeError('The promise and the return value are the same')); } if (x instanceof MPromise) { // 如果 x 为 Promise ,则使 newPromise 接受 x 的状态 // 也就是继续执行 x,如果执行的时候拿到一个 y,还要继续解析 y queueMicrotask(() => { x.then((y) => { this.resolvePromise(promise2, y, resolve, reject); }, reject); }) } else if (typeof x === 'object' || this.isFunction(x)) { // 如果 x 为对象或者函数 if (x === null) { // null 也会被判断为对象 return resolve(x); } let then = null; try { // 把 x.then 赋值给 then then = x.then; } catch (error) { // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise return reject(error); } // 如果 then 是函数 if (this.isFunction(then)) { let called = false; // 将 x 作为函数的作用域 this 调用 // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise try { then.call( x, // 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise (y) => { // 需要有一个变量 called 来保证只调用一次. if (called) return; called = true; this.resolvePromise(promise2, y, resolve, reject); }, // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise (r) => { if (called) return; called = true; reject(r); }); } catch (error) { // 如果调用 then 方法抛出了异常 e: if (called) return; // 否则以 e 为据因拒绝 promise reject(error); } } else { // 如果 then 不是函数,以 x 为参数执行 promise resolve(x); } } else { // 如果 x 不为对象或者函数,以 x 为参数执行 promise resolve(x); } } ``` 9. onFulfilled 和 onRejected 是微任务 咱们可以用 queueMicrotask 包裹执行函数 ```js const fulfilledMicrotask = () => { queueMicrotask(() => { try { const x = realOnFulfilled(this.value); this.resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }) }; const rejectedMicrotask = () => { queueMicrotask(() => { try { const x = realOnRejected(this.reason); this.resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) } ``` 10. 简单写点代码测试一下 ```js const test = new MPromise((resolve, reject) => { setTimeout(() => { resolve(111); }, 1000); }).then(console.log); console.log(test); setTimeout(() => { console.log(test); }, 2000) ``` ``` 这个时候同学们会发现, 为什么我可以调用.then, 不可以调用.catch 呢? 因为我们并没有在类里面声明 catch 方法 ``` 11. catch 方法 ```js catch (onRejected) { return this.then(null, onRejected); } ``` 12. promise.resolve 将现有对象转为 Promise 对象,如果 Promise.resolve 方法的参数,不是具有 then 方法的对象(又称 thenable 对象),则返回一个新的 Promise 对象,且它的状态为 fulfilled。 注意这是一个静态方法, 因为咱们是通过 Promise.resolve 调用的, 而不是通过实例去调用的. ```js static resolve(value) { if (value instanceof MPromise) { return value; } return new MPromise((resolve) => { resolve(value); }); } ``` 13. promise.reject 返回一个新的 Promise 实例,该实例的状态为 rejected。Promise.reject 方法的参数 reason,会被传递给实例的回调函数。 ```js static reject(reason) { return new MPromise((resolve, reject) => { reject(reason); }); } ``` 14. promise.race `const p = Promise.race([p1, p2, p3]);` 该方法是将多个 Promise 实例,包装成一个新的 Promise 实例。 只要 p1、p2、p3 之中有一个实例率先改变状态,p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数。 ```js static race(promiseList) { return new MPromise((resolve, reject) => { const length = promiseList.length; if (length === 0) { return resolve(); } else { for (let i = 0; i < length; i++) { MPromise.resolve(promiseList[i]).then( (value) => { return resolve(value); }, (reason) => { return reject(reason); }); } } }); } ``` ## Promise 面试题 1. 为什么 promise resolve 了一个 value, 最后输出的 value 值确是 undefined ```js const test = new MPromise((resolve, reject) => { setTimeout(() => { resolve(111); }, 1000); }).then((value) => { console.log('then'); }); setTimeout(() => { console.log(test); // MPromise { // FULFILLED_CALLBACK_LIST: [], // REJECTED_CALLBACK_LIST: [], // _status: 'pending', // value: null, // 为什么是空? // reason: null // } }, 3000) ``` 答: 因为现在这种写法, 相当于在.then 里 return undefined, 所以最后的 value 是 undefined. 如果显式 return 一个值, 就不是 undefined 了;比如 return value. 2. then 返回的是一个新 Promise, 那么原来 promise 实现的时候, 用数组来存回调函数有什么意义? 这个问题提出的时候, 应该是有一个假定条件, 就是**链式调用**的时候. 这个时候, 每一个.then 返回的都是一个新 promise, 所以每次回调数组 FULFILLED_CALLBACK_LIST 都是空数组. 针对这种情况, 确实用数组来存储回调没意义, 完全可以就用一个变量来存储。 ```js const test = new MPromise((resolve, reject) => { setTimeout(() => { resolve(111); }, 1000); }).then((value) => { }).then(() => { }) ``` 但是还有一种 promise 使用的方式, 这种情况下, promise 实例是同一个, 数组的存在就有了意义 ```js const test = new MPromise((resolve, reject) => { setTimeout(() => { resolve(111); }, 1000); }) test.then(() => {}); test.then(() => {}); test.then(() => {}); test.then(() => {}); ``` 3. 为什么我在 catch 的回调里, 打印 promise, 显示状态是 pending ```js const test = new MPromise((resolve, reject) => { setTimeout(() => { reject(111); }, 1000); }).catch((reason) => { console.log('报错' + reason); console.log(test) }); setTimeout(() => { console.log(test); }, 3000) ``` 1. catch 函数**会返回一个新的 promise, 而 test 就是这个新 promise** 2. catch 的回调里, 打印 promise 的时候, 整个回调 **还并没有执行完成(所以此时的状态是 pending)** , 只有当整个回调完成了, 才会更改状态 3. catch 的回调函数, 如果成功执行完成了, 会改变这个新 Promise 的状态为 fulfilled 4. 实现 Promise.race ```js race(promiseList) { return new Promise((resolve, reject) => { const length = promiseList.length; if (length === 0) { return resolve(); } else { for (let i = 0; i < length; i++) { Promise.resolve(promiseList[i]).then( (value) => { return resolve(value); }, (reason) => { return reject(reason); }); } } }); } ``` 5. 实现 Promise.all ```js static all(promiseList){ return new MPromise((resolve, reject)=>{ let res = []; let count = 0; for(let i = 0; i < promiseList.length; i++){ // Promise.resolve 解决传参不确定性 Promise.resolve(promiseList[i]) .then((value) => { // 不能使用 push,是因为 all 返回的数据是有顺序的 res[i] = value; count++; // 使用计数的方式,是因为,异步的执行,不能确定谁先,如果 下标为 1 的异步执行了,此时数组就是 arr[0] = undefined arr[1] = value if(count === promiseList.length){ resolve(res) } }) .catch((reason)=>{ // 一旦出错直接抛出 reject(reason) }); } }) } ``` 6. 实现 Promise.prototype.finally() ```js Promise.prototype.finally = function (callback) { return this.then( (value) => { return Promise.resolve(callback()).then(() => value); }, (err) => { return Promise.resolve(callback()).then(() => {throw err}); } ) } ``` 7. 实现 Promise.allSettled() ```js Promise.myAllSettled = (promises) => { return new Promise((resolve, reject) => { const len = promises.length; let count = 0; let result = []; if (len === 0) { return resolve([]); } for(let i = 0; i < promiseList.length; i++){ Promise.resolve(promiseList[i]).then((value) => { count += 1 result[ i ] = { status: 'fulfilled', value: value } }).catch((reason) => { count += 1 result[i] = { status: 'rejected', reason: reason } }).finally(() => { if (count === len) { resolve(result) } }) } }) } ``` 8. 我们用 promise 来解决什么问题? * 回调地狱,代码难以维护 * promise 支持多并发的请求,获取并发请求中的数据 注意:promise 可以解决异步问题,但本身不能说 promise 就是异步的 ## Generator ### 迭代器 Iterator 迭代器 Iterator 是在 ES6 引入的一种新的遍历机制,同时也是一种特殊对象,它具有一些专门为迭代过程设计的专有接口。 每个迭代器对象都有一个 next()方法,每次调用都返回一个当前结果对象。当前结果对象有两个属性 1. value 当前属性的值 2. done 判断遍历是否结束,当没有更多可返回数据时,返回 true 每调用一次 next()方法,都会返回下一个可用的值,直到遍历结束 ### ⽣成器 Generator 生成器是一种返回迭代器的函数 ```js // 通过加星号,方法内部调用 yield 来实现, function* generator() { const list = [1,2,3]; for (let i of list){ yield i; } } let g = generator(); // 通过调用 next,返回下一个值 console.log(g.next()); // {value: 1, done: false} console.log(g.next()); // {value: 2, done: false} console.log(g.next()); // {value: 3, done: false} console.log(g.next()); // {value: undefined, done: true} ``` 特征: * 每当执行完一条 yield 语句就会自动停止执行,直到再次调用 next() * Yield 关键字只可在生成器内部使用,在其他地方使用会导致程序抛出错误 * 可以通过函数表达式的方式来创建生成器,但**不能使用箭头函数创建生成器** ## Async 和 Await async function 和 await 是基于 promise 的语法糖, 使异步代码更易于编写和阅读. ```js async function hello() { return "Hello" } hello().then((value) => console.log(value)) ``` ```js async function hello() { return greeting = await Promise.resolve("Hello"); }; hello().then(alert); ``` [async 和 await:让异步编程更简单(opens new window)](https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Asynchronous/Async_await) ## 事件循环 JavaScript 是单线程,每一个浏览器窗口一个 JS 线程,可以理解为在特定时刻只有特定的代码能够被执行,并阻塞其他代码 浏览器是事件驱动的,很多行为是异步行为,会创建事件到执行队列,而 JavaScript 单线程去执行它的任务队列。比如鼠标点击、定时器触发事件发生、XMLHttpRequest 完成回调,放入队列,等待当前代码执行 浏览器有一个内部大消息循环,Event loop(事件循环),会轮询大的事件队列并处理队列。 虽然 JS 运行在浏览器中是单线程的,但是浏览器本身是多线程,有如下线程: 1. JavaScript 引擎线程 单线程工作,负责解析运行 JavaScript 脚本。和 GUI 渲染线程互斥,JS 运行耗时过长就会导致页面阻塞。 2. GUI 渲染线程 负责渲染页面,解析 HTML,CSS 构成 DOM 树等,当页面重绘或者由于某种操作引起回流都会调起该线程 和 JS 引擎线程是互斥的,当 JS 引擎线程在工作的时候,GUI 渲染线程会被挂起,GUI 更新被放入在 JS 任务队列中,等待 JS 引擎线程空闲的时候继续执行。 3. 事件触发线程 当事件符合触发条件被触发时,该线程会把对应的事件回调函数添加到任务队列的队尾,等待 JS 引擎处理。 4. Http 请求线程 http 请求的时候会开启一条请求线程。请求完成有结果了之后,将请求的回调函数添加到任务队列中,等待 JS 引擎处理。 5. 定时器触发线程 浏览器定时计数器并不是由 JS 引擎计数的,阻塞会导致计时不准确。开启定时器触发线程来计时并触发计时,计时完成后会被添加到任务队列中,等待 JS 引擎处理。 setTimeout(func, 0)有什么用? 告诉浏览器 js 引擎线程,在 0 秒后把 func 放到主事件队列,等待当前代码执行完后再执行,重点就是它会改变代码的执行顺序 作用是:让浏览器跳过 func,渲染当前的变化(JS 引擎执行时,GUI 进程会被阻塞。) 宏任务 macro-task 包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。 微任务 micro-task 包括:process.nextTick, Promises, Object.observe, MutationObserver。 ```js console.log(1); setTimeout(function() { console.log(2); }) var promise = new Promise(function(resolve, reject) { console.log(3); resolve(); }) promise.then(function() { console.log(4); }) console.log(5); ``` 分析: * 第一次循环,整段代码作为宏任务进入主线程执行 * 遇到 setTimeout,就会等过了指定时间后把回调函数放入宏任务的任务队列 * 遇到 Promise,将 then 函数放入到微任务的任务队列 * 整个事件循环完成后,开始检测微任务队列是否存在任务,存在就执行 * 第一次循环得到 1,3,5,4 * 接着再到宏任务任务队列中按顺序取出一个宏任务到栈中让主线程执行,那么在这次循环中的宏任务就是 setTimeOut 注册的回调函数,执行完这个回调函数,发现这个循环中并不存在微任务,就准备进行下一次的事件循环 * 检测到宏任务队列已经没有要执行的任务,就结束事件循环 * 最终得出 1,3,5,4,2
追风者
2022年3月3日 23:05
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
关于 MrDoc
觅思文档MrDoc
是
州的先生
开发并开源的在线文档系统,其适合作为个人和小型团队的云笔记、文档和知识库管理工具。
如果觅思文档给你或你的团队带来了帮助,欢迎对作者进行一些打赏捐助,这将有力支持作者持续投入精力更新和维护觅思文档,感谢你的捐助!
>>>捐助鸣谢列表
微信
支付宝
QQ
PayPal
Markdown文件
分享
链接
类型
密码
更新密码