0
点赞
收藏
分享

微信扫一扫

理解js中的同步和异步

sullay 2022-03-30 阅读 93

首先要先了解下js单线程

一、为什么js是单线程?

二、同步任务和异步任务

(1)为什么会有同步和异步?

因为JavaScript的单线程,因此同个时间只能处理同个任务,所有任务都需要排队,前一个任务执行完,才能继续执行下一个任务,但是,如果前一个任务的执行时间很长,比如文件的读取操作或ajax操作,后一个任务就不得不等着,拿ajax来说,当用户向后台获取大量的数据时,不得不等到所有数据都获取完毕才能进行下一步操作,用户只能在那里干等着,严重影响用户体验。
因此,JavaScript在设计的时候,就已经考虑到这个问题,主线程可以完全不用等待文件的读取完毕或ajax的加载成功,可以先挂起处于等待中的任务,先运行排在后面的任务,等到文件的读取或ajax有了结果后,再回过头执行挂起的任务,因此,任务就可以分为同步任务和异步任务。
其实同步和异步,
无论如何,做事情的时候都是只有一条流水线(单线程),
同步和异步的差别就在于这条流水线上各个流程的执行顺序不同。

(2) 同步任务

同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务,当我们打开网站时,网站的渲染过程,比如元素的渲染,其实就是一个同步任务。

(3) 异步任务

异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程,当我们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务

 <script>
      setTimeout(function(){
         console.log(3);
        },5000)
      console.log(1);
      console.log(2);
 </script>

执行顺序是这样的,先输出1和2等待5秒后输出3
在这里插入图片描述
上述代码具体的执行过程是:

(4) 异步机制

那么,JavaScript中的异步是怎么实现的呢?那要需要说下回调和事件循环这两个概念啦

首先要先说下任务队列,前面也介绍了,异步任务是不会进入主线程,而是会先进入任务队列,任务队列其实是一个先进先出的数据结构,也是一个事件队列,比如说文件读取操作,因为这是一个异步任务,因此该任务会被添加到任务队列中,等到IO完成后,就会在任务队列中添加一个事件,表示异步任务完成啦,可以进入执行栈啦~ 但是这时候,主线程不一定有空,当主线程处理完其它任务有空时,就会读取任务队列,读取里面有哪些事件,排在前面的事件会被优先进行处理,如果该任务指定了回调函数,那么主线程在处理该事件时,就会执行回调函数中的代码,也就是执行异步任务啦

单线程从任务队列中读取任务是不断循环的,每次执行栈被清空后,都会在任务队列中读取新的任务,如果没有任务,就会等待,直到有新的任务,这就叫做任务循环,因为每个任务都是由一个事件触发的,因此也叫做事件循环

总的来说,JavaScript的异步机制包括以下几个步骤

三、js常见的异步操作(个人理解需要耗时很长的操作)

1) setTimeout (setInterval)
2)AJAX
3)事件绑定(如:.on,.bind,.listen,.addEventListener,.observe)
4)观察者模式(例如:websocket中就有发布和订阅之类的操作)
5)promise 是一个异步操作,但是它是一个异步操作的解决方案,即保存着异步操作的结果,可以把异步函数以同步函数的形式写出来(Promise的成功|失败的回调(.then()/.catch())
6)async/await — 使用generator的语法糖,可以理解为是generator的一种改进
7)generator函数 — 使用promise的语法糖,可以理解为是promise的另一种拓展

三、 异步转同步的方式

为了解决异步编程,出现了三种类似的用于解决异步操作的方案。(个人理解这种方式为了解决异步操作的,都是异步操作变成同步;需要取到上个方法返回的值,才能继续正常往下执行,见下方回调函数具体需求的描述)

1)回调函数(回调函数就是将一个函数当作另一个主函数的参数来使用的函数。)

这是异步编程最基本的方法

function test1(callback){ //(主函数)
   console.log('执行了test1');  //主函数任务代码
  setTimeout(function () {
   callback();
  }, 1000);
}
function test2(){//回调函数
   console.log('执行了test2');
}

test1(test2); // 执行

在这里插入图片描述
解读:
回调函数是传统的一种异步编程解决方案,其原理就是将一个函数当作参数传递到另一个主函数中,当主函数执行完自身的内容之后,在运行传递进来的回调函数。

采用这种方式,我们把同步操作变成了异步操作,test1()不会堵塞程序运行,相当于先执行主程序的主要逻辑,将耗时的操作推迟执行。

优点:简单,容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,流程会很混乱
缺点:一个任务只能有一个回调函数

2)promise

已经知道setTimeout是一种异步操作了,因此这里的例子可以将每一个setTimeout拟作一次接口请求

自我理解:

先不用.then()处理异步任务
在这里插入图片描述
在这里插入图片描述
用.then() 处理异步任务
在这里插入图片描述
这样就达成了理想的效果,先执行a()函数,然后执行b()函数。
在这里插入图片描述
3)async-await的基础用法

3.1 async 函数和普通函数一样执行,是generator的语法糖。
3.2 async函数的返回值是Promise 对象,所以可以用.then方法指定下一步的操作,return语句返回的值,会成为then方法回调函数的参数

 async function test() {
    return 'hello world'
}
test().then(result => {
    console.log(result);
})

3.3 基本使用示例:多个异步操作完成后才会进行后续操作

在这里插入图片描述

await后面的函数建议 返回 Promise对象(即return new Promise((resolve,reject)=>{})) 并且主动调用resolve()才能够进行后续的then或者是后续的await操作,倘若是执行了reject(reject的参数会被catch方法的回调函数接收到)或者throw抛出错误之类的就会导致当前执行中断。

举报

相关推荐

0 条评论