异步
JS 是单线程语言,同一时间只能做一件事儿,并且 JS 执行和 DOM 渲染共用一个线程;
JS 异步采用单线程非阻塞式方法实现,基于 event loop 机制;
异步和同步的区别
异步不会阻塞代码的执行,想象一下 console;
同步会阻塞代码的执行,想象一下 alert;
前端使用异步的场景
- 网络请求,如加载图片;
- 定时任务,如 setTimeout;
描述 event loop 的机制(事件循环/事件轮询)
异步使用回调,基于 event loop;
DOM 事件也使用回调,同样基于 event loop;
- 同步代码一行一行依次执行;
- 遇到异步代码则记录下来;
- 同步代码执行完毕后,启动事件轮询机制;
- 获取当前可执行的异步代码并执行;
微任务和宏任务分别有哪些,有什么区别?
- 宏任务:setTimeout、setInterval、ajax、DOM 事件等;
- 微任务:Promise、async、await 等;
- 微任务的执行时机比宏任务要早;
- 微任务在 DOM 渲染前触发;
- 宏任务在 DOM 渲染后触发;
event loop 和 DOM 渲染的关系
- 调用栈空闲时,也就是同步代码执行完毕后;
- 先执行可执行的微任务;
- 然后尝试触发 DOM 渲染;
- 接着执行可执行的宏任务;
- 循环往复执行 2~4 步;
promise 的三种状态
- 只能由 pending 变为 resolved(fulfilled);
- 或由 pending 变为 rejected;
- 且变化不可逆;
then 和 catch 对状态的影响(重要)
- then 在内部代码执行完毕后,返回 resolved 状态的 promise,代码执行出错则返回 rejected 状态的 promise;
- catch 在内部代码执行完毕后,返回 resolved 状态的 promise,代码执行出错则返回 rejected 状态的 promise;
- then 和 catch 都可以触发 then 回调,也都有可能触发 catch 回调;
用几道题目来理解上面的内容:
第一题
1 2 3 4 5 6 7 8 9 10 11 12
| Promise.resolve() .then(() => { console.log(1); }) .catch(() => { console.log(2); }) .then(() => { console.log(3); });
|
then 和 catch 就像 switch 中的 case 一样,不会因为位置的变化被阻断,而是会依次匹配符合当前状态的选项;
第二题
1 2 3 4 5 6 7 8 9 10 11 12 13
| Promise.resolve() .then(() => { console.log(1); throw new Error("then error"); }) .catch(() => { console.log(2); }) .then(() => { console.log(3); });
|
then 会触发 catch,catch 也可以触发 then,匹配结果取决于内部代码的执行状态,而不是 then 和 catch 本身的语义;
第三题
1 2 3 4 5 6 7 8 9 10 11 12 13
| Promise.resolve() .then(() => { console.log(1); throw new Error("then error"); }) .catch(() => { console.log(2); }) .catch(() => { console.log(3); });
|
catch 中的代码执行没有产生错误,返回的也是 resolved 状态的 promise;
什么是 async / await
用同步的语法编写异步的代码,彻底消灭了回调,但本质上还是异步;
1 2 3 4 5 6 7 8 9 10 11 12 13
| async function async1() { console.log("async1 start"); await async2(); console.log("async1 end"); }
async function async2() { console.log("async2"); }
console.log("script start"); async1(); console.log("script end");
|
执行结果如下:
await 必须在 async 函数中使用;
await 后面的代码可以认为是 callback 中的代码,await 不返回,后面的代码不执行;
await 后面跟 promise 对象时,执行与否取决于 promise 对象的状态,如下:
1 2 3 4 5
| (async function () { const p = new Promise(() => {}); await p; console.log("p"); })();
|
await 后面跟非 promise 对象时,直接返回,如下:
1 2 3 4
| (async function () { const res = await 100; console.log(res); })();
|
await 只能处理 resolved 状态的 promise,如下:
1 2 3 4 5
| (async function () { const p = Promise.reject("some error"); const res = await p; console.log(res); })();
|
async / await 和 promise 的关系
- 执行 async 函数返回的是 promise 对象;
- await 相当于 promise 的 then;
- await 无法处理 rejected 状态的 promise;
- try…catch 可以捕获异常,代替 promise 的 catch;
经典面试题
复习检查一下自己是否完全理解了;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| async function async1() { console.log("async1 start"); await async2(); console.log("async1 end"); }
async function async2() { console.log("async2"); }
console.log("script start");
setTimeout(function () { console.log("setTimeout"); }, 0);
async1();
new Promise(function (resolve) { console.log("promise1"); resolve(); }).then(function () { console.log("promise2"); });
console.log("script end");
|
执行结果如下:
for…of
for…of 遍历是异步循环;
其他遍历是同步循环,如 for…in、forEach 等;
手写 promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
| class MyPromise { state = "pending"; value = undefined; reason = undefined;
resolveCallbacks = []; rejectCallbacks = [];
constructor(fn) { const resolveHandler = (value) => { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.resolveCallbacks.forEach((fn) => fn(this.value)); } }; const rejectHandler = (reason) => { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.rejectCallbacks.forEach((fn) => fn(this.reason)); } };
try { fn(resolveHandler, rejectHandler); } catch (error) { rejectHandler(error); } }
then(fn1, fn2) { fn1 = typeof fn1 === "function" ? fn1 : (v) => v; fn2 = typeof fn2 === "function" ? fn2 : (e) => e;
if (this.state === "pending") { return new MyPromise((resolve, reject) => { this.resolveCallbacks.push(() => { try { const newValue = fn1(this.value); resolve(newValue); } catch (error) { reject(error); } }); this.rejectCallbacks.push(() => { try { const newReason = fn2(this.reason); reject(newReason); } catch (error) { reject(error); } }); }); }
if (this.state === "fulfilled") { return new MyPromise((resolve, reject) => { try { const newValue = fn1(this.value); resolve(newValue); } catch (error) { reject(error); } }); } if (this.state === "rejected") { return new MyPromise((resolve, reject) => { try { const newReason = fn2(this.reason); reject(this.reason); } catch (error) { reject(error); } }); } }
catch(fn) { return this.then(null, fn); } }
MyPromise.resolve = (value) => { return new MyPromise((resolve, reject) => resolve(value)); }; MyPromise.reject = (reason) => { return new MyPromise((resolve, reject) => reject(reason)); }; MyPromise.all = (promiseList = []) => { return new MyPromise((resolve, reject) => { const result = []; const length = promiseList.length; let resolved = 0; 计数器;
promiseList.forEach((p) => { try { p.then((data) => { result.push(data); resolved++; if (resolved === length) { resolve(result); } }); } catch (error) { reject(error); } }); }); }; MyPromise.race = (promiseList = []) => { return new MyPromise((resolve, reject) => { let resolved = false; promiseList.forEach((p) => { try { p.then((data) => { if (!resolved) { resolve(data); resolved = true; } }); } catch (error) { reject(error); } }); }); };
|