• 《异 步》


    异步

    1. 什么是同步和异步

    同步:函数执行结束立刻有返回值,函数后面的代码需要等待函数有返回值之后才能执行

    function sum (a,b) {
        console.log(a+b)
    	return a+b;
    }
    sum(1,2);
    console.log(1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LleH3r9G-1666525610168)(同步.png)]

    异步:函数执行结束需要等待某个条件满足之后才有返回值,函数后面的代码无需等待函数有返回就能执行

    setTimeout(function(){
        console.log(1)
    },1000)
    cosnole.log(2)	
    
    • 1
    • 2
    • 3
    • 4
    var btn = docment.querySelector('button');
    btn.onclick = function(){
    	console.log(1)
    }
    console.log(2)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2. javaScript中常用的异步操作

    • 定时器 setInterval、settimeout
    • 事件
    • ajax请求
    • promise

    3. V8引擎如何实现异步

    3.1 事件循环

    执行js代码的时候,遇见同步任务,直接推入调用栈中执行,遇到异步任务,将该任务挂起,等到异步任务有返回之后推入到任务队列中,当调用栈中的所有同步任务全部执行完成,将任务队列中的任务按顺序一个一个的推入并执行

    3.2 定时器

    setTimeout(function(){
        console.log(1)
    },1000)
    cosnole.log(2)	
    
    • 1
    • 2
    • 3
    • 4

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AyeSw9TS-1666525610169)(异步.png)]

    3.3 Dom事件

    var btn = docment.querySelector('button');
    btn.onclick = function(){
    	console.log(1)
    }
    console.log(2)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8kREIx0R-1666525610170)(dom事件异步.png)]

    **浏览器进程。**主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。

    **渲染进程。**核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。

    **GPU 进程。**其实,Chrome 刚开始发布的时候是没有 GPU 进程的。而 GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。

    **网络进程。**主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。

    **插件进程。**主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。

    3.4 ajax请求

    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'demo-data', true);
    xhr.send();
    xhr.onreadystatechange = function (e) {
        if (xhr.readyState == 4 && xhr.status == 200) {
            console.log(xhr.responseText);
            console.log(2);
        }
    };
    console.log(1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RJa8L3vY-1666525610170)(ajax异步.png)]

    var a = 1;
    function sum (a,b) {
        console.log(1)
    	return a+b;
    }
    sum(1,2);
    setTimeout(function(){
        console.log(2)
    },1000)
    cosnole.log(3)
    var btn = docment.querySelector('button');
    btn.onclick = function(){
    	console.log(1)
    }
    console.log(2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    消息队列中的任务类型

    • 内部消息类型:输入事件(鼠标滚动、点击、移动)、微任务、文件读写、WebSocket、定时器等。
    • 与页面相关的事件:JavaScript执行、解析DOM、样式计算、布局、CSS动画等。

    消息队列中的任务分成:

    • **宏任务:**消息队列中的任务称为宏任务,每个宏任务中都包含了一个微任务队列。

    • **微任务:**等宏任务中的主要功能都完成后,渲染引擎不急着去执行下一个宏任务,而是执行当前宏任务中的微任务

    宏任务(macrotask)微任务(microtask)
    谁发起的宿主(Node、浏览器)JS引擎
    具体事件1. 执行script标签内部代码
    2.setTimeout/setInterval
    3. ajax请求
    4.postMessageMessageChannel
    5. setImmediate,I/O(Node.js)
    1. Promise
    2. MutaionObserver
    3. Object.observe
    4.process.nextTick(Node.js)

    3.5 Promise

    setTimeout(()=>{console.log(3)},0)
    new Promise(function(resolve,reject){
    	console.log(1);
    	resolve('fullied')
    }).then(function(data){
    	console.log(data)
    })
    console.log(2)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rPnFeWwj-1666525610171)(微任务.png)]

    4. 详解回调地狱解决方案

    4.1 异步回调问题

    第一是嵌套调用,下面的任务依赖上个任务的请求结果,并在上个任务的回调函数内部执行新的业务逻辑,这样当嵌套层次多了之后,代码的可读性就变得非常差了。

    第二是任务的不确定性,执行每个任务都有两种可能的结果(成功或者失败),所以体现在代码中就需要对每个任务的执行结果做两次判断,这种对每个任务都要进行一次额外的错误处理的方式,明显增加了代码的混乱程度

     function ajax(request, resolve, reject) {
           var xhr = new XMLHttpRequest();
           xhr.open(request.method, request.url, request.sync);
           xhr.send();
           xhr.onreadystatechange = function (e) {
               if (xhr.readyState == 4) {
                   if (xhr.status == 200) {
                        resolve(xhr.response)
                   } else {
                        reject('error')
                   }
                }
            };
    }
    ajax({ method: 'GET', url: '/1', sync: true }, function (success) {
    	console.log('success1:' + success)
        ajax({ method: 'GET', url: '/2', sync: true }, function (success) {
        	console.log('success2:' + success)
            ajax({ method: 'GET', url: '/3', sync: true }, function (success) {
            	console.log('success3:' + success)
    		}, function (error) {
                console.log('error:' + error)
            })
        }, function (error) {
             console.log('error:' + error)
        })
    }, function (error) {
         console.log('error:' + error)
    })
    
    • 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

    4.2 promise解决问题

    第一是消灭嵌套调用;

    第二是合并多个任务的错误处理。

    function xFetch(request) {
                function ajax(resolve, reject) {
                    var xhr = new XMLHttpRequest();
                    xhr.open(request.method, request.url, request.sync);
                    xhr.send();
                    xhr.onreadystatechange = function (e) {
                        if (xhr.readyState == 4) {
                            if (xhr.status == 200) {
                                resolve(xhr.response)
                            } else {
                                reject('error')
                            }
                        }
                    };
                }
                return new Promise(ajax)
            }
            var p1 = xFetch({ method: 'GET', url: '/1', sync: true });
            var p2 = p1.then(function(success){
                console.log(success);
                return xFetch({ method: 'GET', url: '/2', sync: true });
            })
            var p3 = p2.then(function(success){
                console.log(success);
                return xFetch({ method: 'GET', url: '/3', sync: true });
            })
            p3.then(function(success){
                console.log(success);
            })
            p3.catch(function(error){
                console.log(error)
            })
    
    • 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
    • 首先我们引入了 Promise,在调用 XFetch 时,会返回一个 Promise 对象。
    • 构建 Promise 对象时,需要传入一个 ajax 函数,XFetch 的主要业务流程都在 ajax 函数中执行。
    • 如果运行在 excutor 函数中的业务执行成功了,会调用 resolve 函数;如果执行失败了,则调用 reject 函数
    • 在 excutor 函数中调用 resolve 函数时,会触发 promise.then 设置的回调函数;而调用 reject 函数时,会触发 promise.catch 设置的回调函数。

    4.3 相关练习题

    4.4 async await

    async await实现是通过Generator(生成器)promise两种技术

    生成器函数

    Generator 的底层实现机制——协程(Coroutine)

    协程是一种比线程更加轻量级的存在。你可以把协程看成是跑在线程上的任务,一个线程上可以存在多个协程,但是在线程上同时只能执行一个协程,比如当前执行的是 A 协程,要启动 B 协程,那么 A 协程就需要将主线程的控制权交给 B 协程,这就体现在 A 协程暂停执行,B 协程恢复执行;同样,也可以从 B 协程中启动 A 协程。通常,如果从 A 协程启动 B 协程,我们就把 A 协程称为 B 协程的父协程。

    function* genDemo() {
        console.log("开始执行第一段");
        yield 'generator 2';
        console.log("开始执行第二段");
        yield 'generator 2';
        console.log("开始执行第三段");
        yield 'generator 2';
        console.log("执行结束");
        return 'generator 2'
    }
    console.log('main 0');
    let gen = genDemo();
    console.log(gen.next().value);
    console.log('main 1');
    console.log(gen.next().value);
    console.log('main 2');
    console.log(gen.next().value);
    console.log('main 3');
    console.log(gen.next().value);
    console.log('main 4')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    async

    async函数返回一个promise

    async function foo(){
    	return 1
    }
    console.log(foo())
    
    • 1
    • 2
    • 3
    • 4
    await
    async function foo() { 
        console.log(1);
        let a = await 100;
        console.log(a);
        console.log(2)
    }
    console.log(0);
    foo();
    console.log(3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    当执行到await 100时,会默认创建一个 Promise 对象,代码相当于

    let promise_ = new Promise((resolve,reject){ 
           resolve(100)
    })
    
    • 1
    • 2
    • 3

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OABRSfpu-1666525610171)(async await.png)]

    promise与async await

    1. promise

    在这里插入图片描述

    1. promise语法

      const p1 = new  Promise((reslove,reject)=>{
              console.log(2);
              reslove(1)
      }).then((data)=>{
            console.log(3);
      }).catch((data)=>{
            console.log(3);
      })
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

    ​ 2. 三种状态

    ​ pending: 在过程中还没有结果

    ​ fulfilled: 已经解决了

    ​ rejected:被拒绝了,失败了

    ​ 3. 状态的变化和表现

    ​ (1)状态一旦生成,不会因为后面再调用的resolve或者reject而改变

    ​ (2)不执行resolve或者reject就一直是pending状态,pending不会触发then和catch

    ​ (3) resolve 方法的参数是then中回调函数的参数

    ​ reject 方法中的参数是catch中的参数

    ​ (4) Promise.resolve()返回fulfilled状态的promise

    ​ Promise.rejetc()返回rejected状态的promise

    ​ (5) then 和 catch 只要不报错,返回的都是一个fullfilled状态的promise

    2. promise.all

    2.1 promise.all基本使用

    1. promise.all 基本语法

      Promise.all([p1, p2]).then((result) => {
           console.log(result)              
       }).catch((error) => {
          console.log(error)
      })
      
      • 1
      • 2
      • 3
      • 4
      • 5
    2. promise特点

      • Promise.all可以将多个Promise实例包装成一个新的Promise实例
      • 当参数中的所有promise全部成功时返回成功结果,且结果是所有promise返回成功结果的数组
      • 只要数组中有一个promise失败,则返回失败,结果是第一个失败的promise的值
    3. 使用场景: 如果有一个接口需要等待,页面上两个接口都返回数据才调用,有一个失败就不调用的情况

    2.2 手写promise.all

    ​ 思路:

    ​ 1、接收一个 Promise 实例的数组或具有 Iterator 接口的对象,

    ​ 2、遍历每一个数组元素,如果元素不是 Promise 对象,则使用 Promise.resolve 转成 Promise 对象

    ​ 3、如果全部成功,状态变为 resolved,返回值将组成一个数组传给回调

    ​ 4、只要有一个失败,状态就变为 rejected,返回值将直接传递给回调all()

    ​ 的返回值也是新的 Promise 对象

    function promiseAll(promises) {
                return new Promise(function (resolve, reject) {
                    if (!Array.isArray(promises)) {
                        return reject(new TypeError('arguments must be an array'));
                    }
                    var resolvedCounter = 0;
                    var promiseNum = promises.length;
                    var resolvedValues = new Array(promiseNum);
                    for (let i = 0; i < promiseNum; i++) {
                        Promise.resolve(promises[i]).then(function (value) {
                            resolvedCounter++
                            resolvedValues[i] = value
                            if (resolvedCounter == promiseNum) {
                                return resolve(resolvedValues)
                            }
                        }, function (reason) {
                            return reject(reason)
                        })
                    }
                })
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3. promise.race

    3.1 基本使用

    1. 基本语法

       Promise.race([p1, p2]).then((result) => {
                  console.log(result)
       }).catch((error) => {
                console.log(error)  // 打开的是 'failed'
       })
      
      • 1
      • 2
      • 3
      • 4
      • 5
    2. 特点:Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态

    3.2 手写promise.race

    思路:

    ​ 返回一个创建的promise,在该promise的then中遍历每一个数组元素,并且当

    ​ 某个元素有返回,直接执行创建的promise的resolve/reject

     function promiseRace(promises) {
                if (!Array.isArray(promises)) {
                    throw new Error("promises must be an array")
                }
                return new Promise(function (resolve, reject) {
                    promises.forEach(p =>
                        Promise.resolve(p).then(data => {
                            resolve(data)
                        }, err => {
                            reject(err)
                        })
                    )
                })
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4. async await

    4.1 基本语法

    async function async1(){
                console.log('async1 start');
                await async2();
                console.log('async1 end');
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.2 特点

    1. await关键字只能在async函数中使用

    2. await 后面跟着的应该是一个promise对象

    3. await 表示在这里等待promise返回结果了,再继续执行。

    4. await 相当于promise的then情况,所以promise返回catch没办法处理,所以捕获问题需要配合trycatch使用

  • 相关阅读:
    胡焕庸线,我国东西地级市分布密度分界线
    Dynaform 7.0安装说明教程
    (12)使用TSQL语句 ALTER VIEW 修改v_stu_i视图,使其具有列名学号、姓名、性 政治面貌,补全以下横线处的语句。
    c语言中*p1++和p1++有啥区别
    Redis初步学习
    《下一代互联网(IPv6)搭建与运维》1+X证书
    Air780E模块休眠控制应用指南
    【JS高级】ES5标准规范之数组高阶函数的应用_11
    多线程并发环境下,数据的安全问题&&线程池
    ERPS环网络端口角色
  • 原文地址:https://blog.csdn.net/qq_46262422/article/details/127479424