详解JS中的同步异步编程

JS中的同步异步编程

  • 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'); // 不执行,因为上面的死循环一直占用着“JS渲染线程”,线程空闲不下来,就处理不了其他的事情
// 真实项目中应避免死循环
*/

/* 
 * 定时器的异步编程
     + 设置定时器任务是同步的
     + “间隔interval这么长时间,执行定时器绑定的函数”这个任务是异步的
     + 遇到异步任务,浏览器不会等待它执行完,而是继续执行下面的同步任务;等到所有同步任务执行完成,时间也到达了执行的条件,才会把异步任务执行
*/
/* setTimeout(()=>{
  console.log('Ok');
}, 1000);
console.log('NO'); */

// interval设置为0也不是立即执行
/* setTimeout(()=>{
  console.log('OK')
},0);
console.log('NO'); */

/* 
  * 浏览器加载页面,默认开辟一个任务队列(是一个堆内存,不是线程) 
     Event Queue(存放异步任务)
       + 微任务
       + 宏任务:定时器绑定的方法(函数)
   
  * 当栈中的同步任务或者其他任务没有执行完之前,JS渲染线程不会空闲下来,此时哪怕定时器已经到达指定时间,也不会执行(因为JS是单线程的)

  * 异步任务队列中
     + 有微任务,先执行微任务
     + 没有微任务,再执行宏任务
     + 执行顺序,谁先到达时间先执行谁
     + 并且都是把异步任务放到栈内存中去执行
 */

setTimeout(()=>{ // 定时器1
  console.log(1)
},20);
console.log(2);
setTimeout(()=>{ // 定时器2
  console.log(3);
},10);
console.log(4);
console.time('a');
for(let i=0; i<90000000; i++) {}
console.timeEnd('a'); // 50 ms左右,这
console.log(5);
setTimeout(() =>{ // 定时器3
  console.log(6);
},8);
console.log(7);
setTimeout(() =>{ // 定时器4
  console.log(8);
},15);
console.log(9); 
// 先执行同步任务:2 4 5 7 9
// 再执行异步任务:3 1 6 8
// 所以答案是:2 4 5 7 9 3 1 6 8
/* 
 * 解析:
    + 循环结束的时候前面定时器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 是回调函数(必须要传递)
*/

// let p = new Promise(); // 报错 Promise resolver undefined is not a function

/* 
 * 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);
// 先输出1,再输出2  (证明Promise是同步的)

console.log(Object.prototype.toString.call(p1)); // '[object Promise]'
console.dir(p1);
 */

/* let p1 = new Promise(function(resolve, reject){
  resolve(321); // 改变状态后,下面再改变状态就无用了
  reject(123); // 这里不会执行,因为已经执行 resolve了
});
console.log(p1); */

/* 
 * Promise.prototype 上有几个重要方法
    + then([A,B]):基于then方法存放两个回调函数A、B,当实例的状态是成功则执行函数A;状态为失败则执行函数B
    + catch
    + finally
*/

let p1 = new Promise((resolve, reject)=> {
  // Promise中的 resolve/reject 是异步任务(微任务)
  console.log(a); // 报错 a is not defined => p1的状态为失败(rejected),值为失败的原因 'a is not defined'
  resolve('OK');
  // 同步任务执行完之后,去找异步任务 resolve,执行这个异步任务的时候
  // + 先更改当前实例状态为 fulilled
  // + 把 OK 赋值给 实例的 [[PromiseValue]]
  // + 通知then方法中的第一个函数(A)执行
});

/* 
 * 执行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;  // 这里报错,所以导致p2的状态为失败,值是失败的原因
});
console.log('哈哈');  // 先输出(这是同步任务)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值