JavaScript高级手记(同步,异步)
1、JS中的同步异步编程
* JS中的同步异步编程
* 【进程、线程】
+ 进程:代表的是一个程序(浏览器新开一个页面就是一个进程)
+ 线程:用来处理进程中的具体事物的,如果一个程序中需要同时做好多事情,就需要开辟好多线程
+ 一个线程同时只能做一件事
* 浏览器是多线程的
+ GUI渲染线程
+ HTTP网络线程
+ JS代码渲染线程
+ ...
* JS是单线程的:浏览器只分配一个线程用来渲染JS代码
+ JS中的代码大部分都是“同步编程”:上面任务没有处理完成,下面的代码时无法进行处理的
+ 但是JS中利用浏览器的多线程机制,可以规划处“异步编程”效果
+ 定时器
+ ajax/Fetch/跨域(HTTP网络请求)
+ 事件绑定
+ Promise中也有异步编程
+ async / await
+ Generator yield
+ ...
* 计算程序执行的时间(预估)
+ 运行监控 console.time/timeEnd(受当前电脑运行环境的影响)
+ 大O表示法(提前预估)
console.time('a');
for(let i=0; i<99999999; i++) {}
console.timeEnd('a');
while(true){}
console.log('Ok');
* 定时器的异步编程
+ 设置定时器任务是同步的
+ “间隔interval这么长时间,执行定时器绑定的函数”这个任务是异步的
+ 遇到异步任务,浏览器不会等待它执行完,而是继续执行下面的同步任务;等到所有同步任务执行完成,时间也到达了执行的条件,才会把异步任务执行
setTimeout(()=>{
console.log('Ok');
}, 1000);
console.log('NO'); */
setTimeout(()=>{
console.log('OK')
},0);
console.log('NO');
* 浏览器加载页面,默认开辟一个任务队列(是一个堆内存,不是线程)
Event Queue(存放异步任务)
+ 微任务
+ 宏任务:定时器绑定的方法(函数)
* 当栈中的同步任务或者其他任务没有执行完之前,JS渲染线程不会空闲下来,此时哪怕定时器已经到达指定时间,也不会执行(因为JS是单线程的)
* 异步任务队列中
+ 有微任务,先执行微任务
+ 没有微任务,再执行宏任务
+ 执行顺序,谁先到达时间先执行谁
+ 并且都是把异步任务放到栈内存中去执行
setTimeout(()=>{
console.log(1)
},20);
console.log(2);
setTimeout(()=>{
console.log(3);
},10);
console.log(4);
console.time('a');
for(let i=0; i<90000000; i++) {}
console.timeEnd('a');
console.log(5);
setTimeout(() =>{
console.log(6);
},8);
console.log(7);
setTimeout(() =>{
console.log(8);
},15);
console.log(9); */
* 解析:
+ 循环结束的时候前面定时器1和定时器2都已经到达指定时间了,但是不执行,因为同步任务还没有执行结束;
+ 同步任务执行结束,再执行异步任务:在存放定时器3的时候,定时器1和定时器2都已经到达指定时间了,也就是说定时1和定时器2已经满足执行条件,因此先执行,但是定时器2又比定时器1先到达时间,所以先执行定时器2,再执行定时器1;接着是定时器3和定时器4,谁先到达时间谁先被执行
2、Promise的基础知识
* Promise语法:因为Promise是内置类,所以使用的时候得通过new创建其实例
let p1 = new Promise([Fn]);
+ Promise 是一个内置类
+ p1 是类的一个实例
+ Fn 是回调函数(必须要传递)
* Promise 本身是同步的(只是用来管理异步编程)
+ new Promise 的时候会立即把回调函数Fn立即执行,同时黑还会给Fn传递两个函数
+ resolve函数:函数执行会把当前实例的状态修改为成功,并且还会把执行resole函数时传递的值,赋值给实例的 [[PromiseValue]] 属性
+ reject函数:resolve函数:函数执行会把当前实例的状态修改为失败,并且还会把执行reject函数时传递的值,赋值给实例的 [[PromiseValue]] 属性
+ 这两个函数在同一个实例的回调函数中只会执行其中的一个,不会同时执行两个(哪怕两个都写了,第二个也不会执行)
+ 创建一个Promise实例,每个Promise实例有两个属性(很重要)
[promise实例]:有两个私有属性
+ [[PromiseStatus]]: 当前promise实例的状态
+ pendind:初始状态
+ fulfilled/resolved:成功状态(一般指的是异步请求成功)
+ rejected:失败状态(一般指的是异步请求失败)
+ 一旦状态从 pending 变为 fulilled 或者 rejected,那么实例的状态就不会再改变的,即状态改变的单向的(只能 pending -> fulilled 或者 pending -> rejected)
+ [[PromiseValue]]: 当前promise实例的结果/值
+ 初始值是undefined
+ 不论成功还是失败,成功的结果或者失败的原因都会赋值给它
let p1 = new Promise(function(){
console.log(1);
});
console.log(2);
console.log(Object.prototype.toString.call(p1));
console.dir(p1);
let p1 = new Promise(function(resolve, reject){
resolve(321);
reject(123);
});
console.log(p1); */
* Promise.prototype 上有几个重要方法
+ then([A,B]):基于then方法存放两个回调函数A、B,当实例的状态是成功则执行函数A;状态为失败则执行函数B
+ catch
+ finally
let p1 = new Promise((resolve, reject)=> {
console.log(a);
resolve('OK');
});
* 执行then方法会返回一个新的Promise实例
+ 执行then是为了把当前实例成功执行的A以及失败执行的B存储起来
+ 同时返回一个新的Promise实例 p2
+ p2的结果和状态:由上一个实例p1基于then存放的A/B函数执行决定
+ 不论p1基于then执行A还是执行B,只要执行过程中浏览器没有报错,则p2的状态都为成功,否则就为失败
+ p2的值是p1基于then执行A或者B的返回值(如果没有返回值,就默认是undefined)
+ 特殊情况:如果A/B执行时返回了一个新的Promise实例,则返回的实例的状态和结果,会直接决定p2的状态和结果
p1的状态也会受到回调函数Fn执行是否报错的影响,如果执行报错了,则p1的状态就是失败的,值为失败的原因;如果执行不报错,则取决于Fn中执行的是resolve/reject的哪一个
let p2 = p1.then(res =>{
console.log(res);
return 1234;
}, rej=>{
console.log(rej);
return a;
});
console.log('哈哈');