JavaScript异步机制与方案
前言:Javascript语言的执行环境是"单线程"。也就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务。
这种模式虽然实现起来比较简单,执行环境相对单纯,但是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。
为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步和异步。主要介绍异步编程几种办法,并通过比较,得到最佳异步编程的解决方案。
一、同步与异步
1.同步:同步就是后一个任务等待前一个任务执行完毕后,再执行,执行顺序和任务的排列顺序一致。
举个例子:按照排队的顺序,依次打饭,只有前面一个人打完饭后,才轮到后面一个人。
2.异步:异步是非阻塞的,异步逻辑与主逻辑相互独立,主逻辑不需要等待异步逻辑完成,而是可以立刻继续下去。
主逻辑指的是程序中执行同步任务的代码部分,该部分代码会按照代码顺序执行,直到全部执行完毕或者遇到阻塞(例如 I/O 操作)时会暂停执行等待操作完成。
异步逻辑指的是程序中执行异步任务的代码部分,异步逻辑与主逻辑相互独立,不会阻塞主逻辑的执行,异步任务执行完毕后,触发回调函数来通知主逻辑执行后续处理。异步任务可以是网络请求、文件读取、计时器等操作,这些操作都可能需要一定时间才能完成,因此需要异步进行执行。
举个例子:有同学忘记带饭卡了,怎么办?如果一直等着饭卡拿来,那是不是很花时间,后面同学就不能打饭了!所以呢,我让出来,让后面的人先打饭,等我的饭卡到了,我再打饭
二、异步编程
1.回调:回调简单地理解为一个函数作为参数传递给另一个函数
用回调函数来获取异步数据
优点:简单,容易理解。
缺点:代码不优雅,可读性差,不易维护,耦合度高,层层嵌套造成回调地狱。
回调地狱:代码难以维护,我们要获取异步程序的数据,不能用return拿数据,用回调函数拿,就要控制顺序,就要用嵌套,嵌套的多了,代码难以维护。
2.事件监听:
采用事件驱动模式,通过监听事件的触发来执行相应的操作。这种方式可以避免阻塞主线程,提高程序的响应速度和效率。
任务的执行不取决代码的顺序,而取决于某一个事件是否发生。
比如onload加载图片,addEvenListener监听点击事件
优点: 比较容易理解,可以绑定多个事件,每一个事件可以指定多个回调函数,而且可以去耦合,有利于实现模块化。
缺点: 整个程序都要变成事件驱动型,运行流程会变得不清晰。
3.promise
Promise 优点和缺点
优点:Promise以同步的方式编写异步代码,避免了回调函数层层嵌套,可读性更强。链式操作,可以在then中继续写Promise对象并return,然后继续调用then进行回调操作。
缺点:Promise对象一旦创建就会立即执行,不能中途取消。如果没有设置回调函数,Promise 会在内部抛出错误,不会向外流。
4.Generator:
概念
Generator函数是es6的,作用是可以将函数执行时可以进行暂停,而普通函数是一直执行到 return,同样也是异步的一种解决方案
定义 Generator 函数
定义函数时通过 function* 进行区分为 Generator 函数
函数内部可以通过 yield 进行暂停 定义普通函数
调用函数
函数直接调用即可,但是函数不会执行,此时只是得到了一个 Generator 对象,需要调用 Generator 对象的 next 方法逐步执行。
yield 向外传递数据
generator 函数可通过 yield 传递数据到函数外,函数外通过调用 next 接收。
向函数内传递数据
第一次 next 传递参数无效,第一次的 next 的参数由函数调用传递
下一次的 next 传递的参数,由上一次的 yield 暂停后重新执行进行返回
处理异步
配合 promise
改成递归方式调用
但是此时没有考虑 co 的返回值为 promise, 所以 co 方法还需返回 promise,一旦返回 promise 又要考虑成功和失败的结果,此时直接使用 co 库。利用 co 库自动执行
利用 co 库自动执行
用于 Generator 函数的自动执行
优点:优雅的流程控制方法,允许函数被中断地执行。
缺点:Generator函数的执行必须依赖executor(执行器),对于只做异步处理还是不太方便。
5.async+await:
async函数对generator函数的改进:
(1)内置执行器:generator函数的执行必须靠执行器,所以才有了co函数库,而async函数自带执行器,也就是说,async函数的执行,与普通函数一模一样,只要一行。
(2)更好的语义。Async和await,比起*和yield,语义更清楚。Async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
async/await的目的为了简化使用基于promise的API时所需的语法。async/await 的行为就好像搭配使用了生成器(generator)和promise。
优点:内置执行器,语义更好,适用性更广。
缺点:误用 await 可能会导致性能问题,因为 await 会阻塞代码。
Async/Await 是一种比较新的异步编程技术,它使异步代码看起来像同步代码,更加直观和易于理解。Async/Await 的实现原理是 Generator 函数和 Promise,它通过同步的方式实现异步。使用 Async/Await 可以避免回调地狱和 Promise 层层嵌套的问题。Async/Await 通常用于处理多个异步操作的情况,这样的代码看起来非常简单和直观。
js中异步的应用场景
定时任务:setTimeout、setInterval
网络请求:ajax请求、动态创建img标签的加载
事件监听器:addEventListener