第12章 异步
setTimeout
- 设置一个定时器,在定时器到期后执行其中的回调函数
- setTimeout 只会执行一次
声明定时器
js
// 以下代码输出顺序依次为
// 第一行代码
// 第二行代码
// 1秒后执行
console.log('第一行代码');
let timer = setTimeout(() => {
console.log('1秒后执行');
}, 1000);
console.log('第二行代码');
清除定时器
- clearTiemrout() 用于清除 setTimeout
js
// 加上以下代码后,timer 将不会执行
setTimeout(() => {
clearTimeout(timer);
console.log('中断timer');
}, 500);
setInterval
- setInterval 与 setTimeout类似
- setInterval 每过一段时间就会执行一次
- clearInterval() 可以用来清除定时器
js
let interval = setInterval(() => {
let date = new Date();
console.log(`${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`);
}, 1000);
// 五秒之后取消Interval
setTimeout(() => {
clearInterval(interval);
}, 5000);
Promise
基本概念
- Promise 可以创建自定义的异步操作
- Promise 是一个构造函数
创建 Promise
通过 new 可以创建 Promise 实例对象
Promise 在创建后会立即执行(同步)
resolve:通过 .then 指定的「成功的回调函数」
reject:通过 .then 指定的「失败的回调函数」
js
function odd(number) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (number % 2 !== 0) {
resolve(`${number}是奇数`);
} else {
reject(`${number}不是奇数`);
}
}, 500);
});
}
完成回调
- Promise可以使用 .then 获取执行结果
- .then 接受两个参数
- 第一个为成功的回调函数(必须)
- 第二个为失败的回调函数(非必须)
js
odd(number).then(
(res) => {
console.log('resolve: ' + res);
},
(err) => {
console.log('rejcet: ' + err);
}
);
异常捕获
- reject 用于返回异常信息
- 用 catch 捕获错误信息
- then 失败回调与 catch 的区别:
- then 成功回调中抛出的异常,后面的 catch 能捕获到,而 then 的失败回调捕获不到
- then 的失败回调与 catch 捕获错误信息的时候会采用就近原则
- 建议总是使用 catch 捕获错误信息,而不使用 then 的第二个参数(失败回调)
js
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
reject('执行失败');
}, 1000);
});
promise.catch((error) => {
console.log(error); // 执行失败
});
三种状态
- pending、resolved、rejected
- pending 状态,不会触发 then 和 catch
- resolved 状态,会触发后续的 then 回调
- rejected 状态,会触发后续的 catch 回调
- then 正常返回 resolved,里面有报错则返回 rejected
- catch 正常返回 resolved,里面有报错则返回 rejected
多个 Promise 同时执行
- Promise.all 可以使多个 Promise 同时执行
- 所有异步操作全部完成后,会返回一个整体的大 Promise
- then 之后会返回一个数组,里面为每个 Promise 的返回值
- 返回值的顺序与传入 Promise 时的顺序相同
- Promise.race 也可以使多个 Promise 同时执行
- 第一个 Promise 执行完毕后,返回该 Promise 结果
js
let p1 = new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 1000);
});
let p2 = new Promise((resolve) => {
setTimeout(() => {
resolve(2);
}, 2000);
});
let p3 = new Promise((resolve) => {
setTimeout(() => {
resolve(3);
}, 500);
});
Promise.all([p1, p2, p3]).then((values) => {
console.log(values); // [1, 2, 3]
});
Promise.race([p1, p2, p3]).then((values) => {
console.log(values); // 3
});
async & await
基本概念
- async & await用来简化 Promise 异步操作
- async 创建的函数相当于创建了一个 Promise
使用
- await 可用于接受另一个 async 函数的结果
- await 关键字必须在带 async 的函数中调用
- 出现异常时,可以使用 try...catch 捕获异常
- 第一个 await 之前的代码会同步执行,之后的会异步执行
js
async function async1() {
let result2 = await async2();
let result3 = await async3();
console.log(result2);
console.log(result3);
}
async function async2() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(10);
}, 1000);
});
}
async function async3() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(5);
}, 500);
});
}
async1();
Event Loop
概念
- JavaScript 是一门单线程执行的编程语言,同一时间只能做一件事
- 为了防止某个耗时任务导致程序假死,JavaScript 把任务分为同步任务与异步任务
同步任务 & 异步任务
- 同步任务
- 在主线程上执行
- 只有前一个任务执行完毕,才能执行后一个任务
- 异步任务
- 由宿主环境(浏览器、Node)进行执行
- 当异步任务执行完毕后,会通知主线程执行回调函数
- 其中异步任务又分为宏任务与微任务
宏任务 & 微任务
- 宏任务:DOM 渲染后触发
- 异步 Ajax 请求
- setTimeout、setInterval
- 文件操作
- 微任务:DOM 渲染前触发
- Promise.then、.catch与.finally
- process.nextTick
执行顺序
- 同步任务由主线程依次执行
- 异步任务委托给宿主环境执行
- 已完成的异步任务对应的回调函数,会被加入到任务队列中等待执行
- 同步任务执行完毕后,查看是否有未执行的微任务,有则执行
- 所有微任务执行完毕后,执行下一个宏任务
- 每次宏任务执行完毕后,查看是否有未执行的微任务,有则执行
- 重复以上步骤
预览: