1. 浏览器线程
进程、线程
- 进程代表的是一个程序(浏览器开一个页卡就是一个进程)
- 线程是用来处理进程中的具体事物,如果一个进程中有很多事情要做,就可以开辟很多线程
- 一个线程同时只能做一件事
浏览器是多线程的
- GUI渲染线程
- HTTP网络线程
- JS渲染线程
- 定时器监听线程
- 事件触发线程
JS是单线程的:浏览器只分配一个线程来渲染JS代码
- JS中的代码大部分都是
同步编程,即上面的任务没有完成,下面的任务是无法处理的 - 但是
JS可以利用浏览器的多线程机制,规划出异步编程效果- 定时器、AJAX/Fetch/跨域(HTTP网络请求)、事件绑定、promise中的异步、Generator函数、async/await等
/*
* 计算程序执行的时间(预估)
* + 运行监控 console.time/timeEnd(受当前电脑运行环境的影响)
* + 时间复杂度、大O表示法(提前预估)
*/
console.time('AAA');
for (let i = 0; i < 99999999; i++) {}
console.timeEnd('AAA');
死循环阻塞代码
while (true) {}
console.log('OK'); // 不执行:上述的死循环一直占用这“JS渲染线程”,线程空闲不下来,就处理不了其他的事情
2. 定时器异步编程
2.1 定时器的同步异步
设置定时器任务是同步的间隔interval这么长时间,执行定时器绑定的函数这个任务是异步的- 遇到异步任务,浏览器
不会等待它执行完,则继续渲染下面的代码;当等到下面代码运行完,时间也到达了执行的条件,才会把异步任务执行;
setTimeout(()=>{
console.log('1');
},1000)
console.log('2');
// 2 1
interval设置为零也不是立即执行,而是浏览器都有最快反应时间(谷歌:5~6ms IE:13~17ms),设置为零,最快也需要等到5~6ms左右
setTimeout(() => {
console.log('1');
}, 0);
console.log('2');
// 2 1
2.2 例题
setTimeout(() => {
console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
console.log(3);
}, 10);
console.log(4);
console.time('AA');
for (let i = 0; i < 90000000; i++) {
// do soming
}
console.timeEnd('AA'); //=>AA: 79ms 左右
console.log(5);
setTimeout(() => {
console.log(6);
}, 8);
console.log(7);
setTimeout(() => {
console.log(8);
}, 15);
console.log(9);
3. Promise异步编程
4. async/await异步编程
4.1 await中的异步
async/await是ES7中新提供的语法糖
-
async修饰一个函数:保证函数
返回的是一个promise实例- 和then很相似,函数执行不报错,返回成功的promise实例,报错返回的是失败的
- return的值或者报错的原因就是promise实例的结果
- 如果return的是一个新的promise实例,则实例的结果影响返回值
-
async最常用的应用,是为了修饰函数,让函数中可以使用await(想要使用await,所在的函数
必须是async修饰的)
function func() {
await fn(); //Uncaught SyntaxError: await is only valid in async function
}
await [promise实例]等待promise实例状态为成功的时候,再继续执行await后面的代码- await 修饰
非promise实例的时候,直接算做成功
(async function () {
let x = await 10;
console.log(x); //10
let y = await Promise.resolve(20);
console.log(y); //20
try {
let z = await Promise.reject(30); //await后面的promise如果是失败的,则当前函数中await下面的代码都不会执行
console.log(z);
} catch (err) {
// 基于try catch异常捕获,可以捕获到await后面的promise实例是失败状态下的失败信息(浏览器控制台不会再报错了)
console.log(err); //30
}
})();
4.2 例题
很多人认为async/await是同步操作,其实这种理解是不正确的,await修饰的操作虽然是同步执行的,但是他在asnyc修饰的上下文中还会有一个特殊的操作,那就是将await下面的代码全部转成微任务,并添加到Event Queue中的微任务中。这么说可能有的小伙伴不是非常的理解,我们通过一道字节跳动的面试题来讲解
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');
代码执行结果:'script start' 'async1 start' 'async2' 'promise1' 'script end' 'async1 end' 'promise2' 'setTimeout'
5. 同步异步编程综合
function func1(){
console.log('func1 start');
return new Promise(resolve=>{
resolve('OK');
});
}
function func2(){
console.log('func2 start');
return new Promise(resolve=>{
setTimeout(()=>{
resolve('OK');
},10);
});
}
console.log(1);
setTimeout(async () => {
console.log(2);
await func1();
console.log(3);
}, 20);
for (let i = 0; i < 90000000; i++) {} //循环大约要进行80MS左右
console.log(4);
func1().then(result=>{
console.log(5);
});
func2().then(result=>{
console.log(6);
});
setTimeout(() => {
console.log(7);
}, 0);
console.log(8);
代码的执行结果:1 4 'func1 start' 'func2 start' 8 5 2 'func1 start' 3 7 6