关于异步的详解

导读

首先,要知道JavaScript是一门单线程的语言,要搞明白其为何会设计成单线程,要先来说一下JavaScript的最初应用场景。

其次,在单线程语言中如何处理多线程的任务。

再次,同步和异步的流程以及如何理解异步。

然后,在解决复杂的逻辑业务时,出现了什么样的问题。

再然后,怎样解决这一问题。

=> Promise/.then(),async/await,

最后谈谈微任务及宏任务

JavaScript最初的应用场景

最初,JavaScript的设计是作为浏览器脚本语言实现用户的交互,而为了避免多线程给我们带来很多不必要的麻烦,比如说:一个线程在删除一个节点,而另一个线程在修改这个节点,这时我们应该以哪一条线程为主呢?

因此,设计者将其设计成单线程的语言。

在H5中规定了JS可以拥有多个子线程,但是子线程仍然是依托于主线程的,且不能够操作节点。

为什么要使用多线程

  1. 单线程的缺点:

    单线程有一个“致命”的缺点,就是会造成阻塞;

    因为是单线程,所以在程序运行时是按照先进先出的原则来进行任务处理的,也正是因如此,在主线程遇到了耗时操作后,其后的任务就进入到了等待的状态。如果此时CPU是被占用的,也没什么,但是如果这个耗时任务是一个不占CPU的操作,举个栗子:向服务端请求数据。这个时候CPU出入空闲状态但是程序并没有执行结束,这就形成了阻塞。

  2. 启发:

    在进行耗时操作且影响代码正常运行时,我们可以先不管这个操作,将其挂起。先处理后面的任务,等到主线程清空时,再来执行这个任务。于是出现了同步任务和异步任务。

同步和异步及程序运行流程

关于这一块内容,笔者建议从整体来看,将这一块中的内容结合起来读,可能会更好理解

  1. 我们可以将同步任务理解为在主线程中执行的任务,异步任务理解为在子线程执行。

  2. 同步任务和异步任务的执行过程:

    • 同步任务正常运行,没有特殊情况会一直执行完毕;

    • 当主线程运行到异步任务时,会安排一个子线程去运行异步任务,当异步任务运行结束后,向任务队列发送一个事件。表示该异步任务可以进入主线程执行了。

  3. 当不考虑下面讲的微任务和宏任务时,我们的程序运行时,会先执行同步任务,执行到异步任务后,将其发送到子线程中运行,运行结束会向任务队列发送一个事件。同步任务执行完毕即主线程清空后,主线程会向任务队列询问,是否有接收到事件,如果没有,那么主线程会一直询问,这个过程称为event loop;如果有,就执行这个事件。直到程序全部完成。

事件

在上文我们提到了事件这一概念,可以将事件理解为异步任务返回的一个回调函数,这个回调函数也就是事件会在主线程上运行。异步任务必须有回调函数。

拓展:异步函数必须return一个Promise对象

关于事件,阮一峰前辈是这么写的:

"任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列",等待主线程读取。

“回调地狱”

在实际开发中,为了实现一些逻辑需求,可能会用到多层回调函数嵌套。这个时候会导致代码的可读性很差。人们称之为“回调地狱”

Promise对象

  • 代表的是异步操作 最终完成或失败

  • 目的:将回调函数的多层嵌套形式,拆解成链式调用的形式。

  • 本质:函数返回的对象,在这个对象上绑定回调函数,避免从一开始将回调函数作为参数传入上一层函数。

  • 一个Promise必然会处于这几种状态:

    1. 待定(pending):初始状态,既没有被兑现,也没有被拒绝

    2. 已兑现(fulfilled):意味着操作成功完成 resolve

    3. 已拒绝(rejected):意味着操作失败 reject

  • Promise的使用:一般作为函数的返回值

    const fn = function(){
        return new Promise((resolve,reject)=>{
            if(ture){
                resolve(a);
            }else{
                reject(b);
            }
        })
    }
    ​
    fn()
        .then((res)=>{有返回值的函数})
        .then((res)=>{有返回值的函数})
        .then((res)=>{有返回值的函数})
        ...
        .then(res=>{最后的函数})
    // 如果Promise对象中的请求完成了,那么将resolve的值传给then中的回调函数作为参数执行then方法
    // 可以附加 .catch()在链式结构的末尾,来捕获错误(reject传回的值),并且之后的then不会执行
    // 可以在最后加一个 .finally() 来执行清理操作  并且这个方法不管请求成功与否都会执行

async/await

是Promise的语法糖;让繁琐的then(),和冗长的链式调用可读性变得更长些;

具体使用方式:

//异步函数1
function getData(data){  
    return new Promise((reslove)=>{
        reslove(data)
    })
}
//异步函数2
function sayHello(data){
    return new Promise((reslove)=>{
        reslove(data)
    })
}
​
//异步函数 
async function fn(){
     // await相当于.then()    getData()相当于是回调函数
    // await必须在async修饰的函数体内使用
    const promiseA = await getData('info')
    const promiseB = await sayHello(promiseA);
    console.log(promiseB)
}

谈谈微任务和宏任务

微任务

  1. 注意:Promise对象中的代码是同步的,then()方法中的回调函数才是异步的

  2. then()中的是微任务

宏任务

  1. 定时器是宏任务

代码执行顺序口诀:先同步后异步,先微任务后宏任务

总之,同微宏

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>