您可能知道,Javascript语言的执行环境是“单线程”。所谓‘单线程’就是一次只能完成一个任务。如果有多个任务,必须排队,前一个任务完成,然后执行下一个任务,依此类推。
该模型的优点是实现相对简单,执行环境相对简单。缺点是只要一个任务需要很长时间,后面的任务就必须排队等待,会耽误整个程序的执行。常见的浏览器无响应(feign死亡),往往是因为某个Javascript代码长时间运行(比如无限循环),导致整个页面卡在这个地方,其他任务无法执行。为了解决这个问题,Javascript语言将任务的执行模式分为两种:同步和异步。“同步模式”是前一段的模式,后一个任务等待前一个任务完成后再执行。程序的执行顺序与任务的排列顺序一致、同步。异步模式完全不同。每个任务都有一个或多个回调函数。前一个任务完成后,执行回调函数代替后一个任务,后一个任务在前一个任务完成前执行。因此,程序的执行顺序与任务的顺序不一致,这是异步的。
“异步模式”非常重要。在浏览器端,长时间的操作应该异步执行,以避免浏览器失去响应。最好的例子就是Ajax操作。在服务器端,“异步模式”甚至是唯一的模式,因为执行环境是单线程的。如果允许所有http请求同步执行,服务器的性能将急剧下降,很快就会失去响应。本文总结了“异步模式”编程的四种方法。了解它们可以帮助你写出结构更合理、性能更好、维护更方便的Javascript程序。一、回调函数这是异步编程最基本的方法。假设有两个函数f1和f2,后者等待前者的执行结果。复制代码如下: f1();F2();如果f1是一个耗时的任务,可以考虑重写f1并编写f2作为f1的回调函数。复制代码如下:函数f1(回调)的任务代码回调(){settimeout(函数(){//f1;}, 1000);}执行代码变成如下:复制代码如下: f1(F2);这样我们把同步操作变成异步操作,f1不会阻塞程序的运行,相当于先执行程序的主逻辑,把耗时的操作推迟。回调函数的优点是简单,易于理解和部署,缺点是不利于代码的读取和维护,各个部分耦合性很强,过程会比较混乱,每个任务只能指定一个回调函数。第二,事件监控的另一个思路是采用事件驱动模式。任务的执行不取决于代码的顺序,而是取决于事件是否发生。或者以f1和f2为例。首先,为f1绑定一个事件(这里使用jQuery)。复制代码如下: f1.on('done ',F2);上面的代码行意味着当完成事件发生在f1中时,执行f2。然后,重写f1:复制:函数f1(){ settimeout(function(){//f1)的任务代码f1 . trigger(' done ');}, 1000);} f1.trigger('done ')表示执行完成后,立即触发done事件,从而开始执行f2。这种方法的优点是容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,并且可以解耦,有利于实现模块化。缺点是整个程序会变成事件驱动,运行过程会变得非常不清晰。第三,发布/订阅上一节的‘事件’,完全可以理解为‘信号’。我们假设有一个“信号中心”。当一个任务完成后,它会向信号中心‘发布’一个信号,其他任务可以向信号中心‘订阅’这个信号,从而知道自己什么时候可以开始执行。这被称为“发布-订阅模式”,也称为“观察者模式”。这个模式有很多实现,下面是Ben Alman的Tiny Pub/Sub,是jQuery的一个插件。首先,f2在“信号中心”订阅来自jQuery的“完成”信号。复制的代码如下:jQuery。订阅(“完成”,F2);然后,f1重写如下:复制代码: function f1(){ settimeout(function(){//f1)的任务代码jquery . publish(' done ');}, 1000);} jQuery.publish('done ')表示f1执行完成后,会向jQuery发出' done '信号,触发f2执行。此外,在f2完成执行后,也可以取消订阅。复制代码如下:jQuery。取消订阅(“完成”,F2);这种方法本质上类似于‘事件监控’,但明显优于后者。因为我们可以通过查看“消息中心”来了解每个信号有多少信号,有多少用户,从而监控程序的运行。4.Promises Object Promises object是CommonJS工作组提出的规范,旨在为异步编程提供统一的接口。简单地说,想法是每个异步任务返回一个Promise对象,该对象有一个允许您指定回调函数的then方法。
比如f1的回调函数f2,可以写成:复制代码的代码如下: f1()。然后(F2);F1应该重写如下(这里使用jQuery的实现):复制代码如下:function f1 () {vardfd=$。递延();SetTimeout(函数(){//f1的任务代码DFD . resolve();}, 500);返回dfd.promise}这样写的好处是回调函数变成了链式写,让程序流程看得一清二楚,并且有一套完整的支持方法,可以实现很多强大的功能。例如,指定多个回调函数:按如下方式复制代码: f1()。然后(f2)。然后(F3);例如,在发生错误时指定回调函数:按如下方式复制代码: f1()。然后(f2)。失败(F3);而且,它还有一个优点是前面三种方法都没有的:如果一个任务已经完成,并且添加了回调函数,那么回调函数就会立即执行。所以,你不必担心错过一个事件或信号。这种方法的缺点是比较难写和理解。