当前位置: 首页 > 科技观察

你还在找什么?JavaScript异步编程全部解决方案在这里

时间:2023-03-15 15:29:29 科技观察

阿里巴巴前端工程师易红详解JavaScript异步编程。JavaScript的特点是单线程。本文首先解读单线程异步原理,然后着重分析JavaScript异步解决方案,详细介绍Callback、Promise、Generator、Async/Await的特点和使用原理。以下是精彩的视频内容安排:单线程异步JavaScript语言的一大特点就是单线程。在特定的时刻,只能执行特定的代码,其他代码都被阻塞。一件事。常用的异步操作主要是网络请求,它有一个响应时间,在响应结果回来之前需要处理其他的东西;IO操作,比如读取一个文件,在进程中做其他事情;定时功能。异步由浏览器的两个或多个常驻线程完成,比如异步请求:JS执行线程发起异步请求,浏览器新开一个http请求执行请求。当它监听到请求已经完成时,会将send函数插入到JS执行队列的尾部等待处理。通过图中所示的定时功能示例,我们可以看到异步处理过程。先打印一个1,然后执行setTimeout函数。约定0秒后打印一个2,再打印一个3,执行结果是先打印1,再打印3,最后打印2,说明整个过程是异步发生的。Javascript有一个主执行线程,但是setTimeout、IO操作、网络异步请求等都会有一个回调函数。这些操作都是通过浏览器API实现的。当定时器时间到或返回请求结果时,回调函数将发送函数推送到一个javascript任务队列中。javascript主线程任务执行完毕后,等待的代码会从任务队列中继续执行。具体过程总结如下:所有同步任务都在主线程上执行,形成一个执行上下文栈。除了主线程之外,还有一个“任务队列”。只要异步任务有运行结果,就会在“任务队列”中放入一个事件。一旦“执行栈”中的所有同步任务都执行完毕,系统就会读取“任务队列”,看看里面有什么事件。那些对应的异步任务结束等待状态,进入执行栈,开始执行。主线程不断重复上面的第三步。异步解决方案了解了Javascript单线程和异步的关系,日常工作中如何写工程代码?异步请求结束后如何执行具体的方法?目前,javascript异步编程已经经历了几个阶段,并且在不断的进化。Callback最初是一个回调函数。当事件完成后,执行回调函数,相当于完成了一个异步操作。我们可以像变量一样使用一个函数,作为另一个函数的参数,作为另一个函数中的返回结果,在另一个函数中调用它。当我们将回调函数作为参数传递给另一个函数时,我们只是传递了函数的定义,而不是在参数中执行它。函数在其参数中定义了回调函数后,可以随时调用(即回调)它。我们工作中接触时间最长的就是发送Ajax异步请求。第一个请求发出后,用它的参数处理第二个请求,然后用第二个请求的参数去请求第三个。代码集如图所示。如图所示,是node.js中mongoDB的一个实例,有六层嵌套。当多个异步事务多级依赖时,回调??函数会形成多级嵌套,代码会变成金字塔结构。虽然可以解决异步问题,但是却让代码难以理解,让调试重构的过程充满了风险。PromisePromise比传统的解决方案更合理、更强大,是一个更好的异步解决方案。promise对象具有以下四个特征:一个promise可能有三种状态:pending、resolved和rejected。promise的状态只能从“waiting”变为“completed”或“rejected”,不能逆转。同时,“完成”状态和“被拒绝”状态不能相互转换。promise对象必须实现then方法,然后必须返回一个promise。同一个promise的then可以被多次调用(链式),回调的执行顺序与定义的顺序相同。then方法接受两个参数,第一个参数是promise成功时的回调,当promise从“waiting”状态变为“completed”状态时调用,另一个是失败时的回调,即当promise从“waiting”状态转换到“reject”状态时被调用,如图调用,在左边创建一个promise对象,2000秒后执行,最后返回promise对象.Promise立即执行。当对象被创建时,代码将被执行直到完成或被拒绝。打印结果在右边。可以看到打印出了创建的promise对象,promise对象生成成功。执行then的时候,此时已经返回了成功状态,所以执行成功的回调函数。那么,promise是如何解决异步问题的呢?有什么特点?方法如下:代码看起来更有逻辑性和可读性。Promise并没有改变JS异步执行的本质,甚至从写法上都能看出一点callback的影子。如图,是一个链式调用方式。第一个请求成功后,会执行下一个。如果任何请求不成功,将直接捕获错误并打印出来。Generator我们希望用同步的方式来写异步代码,这样可以让逻辑更清晰,减少代码量更多。为了实现这个目标,我们发展了一个生成器解决方案。Generator函数很特殊,比promise和callback更难理解。从句法上讲,生成器具有以下特点:定义生成器时,需要使用function*。使用时生成一个Generator对象。执行.next()激活暂停状态,开始执行内部代码,直到遇到yield,此时返回执行结果,并记住此时的执行上下文,暂停。再次执行.next()时重复第三步。如图,首先定义了test=add(5),函数并没有执行,而是生成了一个generator对象,函数处于暂停状态,只有当.next()被执行时,暂停状态才会被激活,内部执行开始,直到遇到第一个yield,代码才会返回执行结果,并记住上下文,暂停,交出控制权。再次执行.next()时,找到第二个yield,再次记住上下文,暂停,交出控制权,重复。如图,封装一个异步任务,定义一个generator对象,执行请求后交接控制权,通过promise判断是否继续执行。异步事件发生后,先把控制权交给别人,让程序执行其他代码,当异步事件完成后,收回控制权,继续执行其他操作。Generator需要一个自动执行器配合使用,实现正常思维下的异步处理。有了自动执行器,就可以把异步的请求写成同步的方式,直接把所有的请求写在一起。经过进一步的演进,async和await函数应运而生。它们各自的特点如下:async表示这是一个async函数,await只能在这个函数中使用。await表示等待await后面的操作完成后,再执行下一行代码。紧跟在await后面的***是一个耗时操作或者异步操作。await后面必须跟一个Promise对象。如果没有,则会转化为一个完成的PromiseAsync,类似于生成器,本质上是生成器的语法糖。其内置的执行器具有更好的语义和更广泛的适用性。返回值是Promise。