JavaScript异步编程

来源:互联网 发布:手机屏幕画笔软件 编辑:程序博客网 时间:2024/06/16 04:18

Javascript语言的执行环境是”单线程”(single thread)。所谓”单线程”,就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。
Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。
“异步模式”非常重要。在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操作。在服务器端,”异步模式”甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有http请求,服务器性能会急剧下降,很快就会失去响应。
然而,异步执行最大的问题就是执行顺序。
假定有两个函数f1和f2,后者等待前者的执行结果。

function f2() {    console.log("f2");}

一、回调函数

function f1(callback) {    console.log("f1");    setTimeout(function(){        callback();    }, 1000);}// 执行f1(f2);

优点是简单、容易理解和部署;
缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。

二、事件监听

这里采用的jQuery的写法

var eventable = {    on: function(event, cb) {        $(this).on(event, cb);    },    trigger: function (event, args) {        $(this).trigger(event, args);    }}var f1 =  {    run: function() {        setTimeout(function(){            // f1执行逻辑            console.log("f1");            f1.trigger("done");        }, 1000);    }};$.extend(f1, eventable);f1.on("done", f2);f1.run();

优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以”去耦合”(Decoupling),有利于实现模块化;
缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。

补充:<注意上述f1的写法>
当使用eval()函数或者是Function构造函数以及使用setTimeout()传一个字符串参数时都会发生“双重解释”。

eval("console.log('Hello Eval!')");var sayHello = new Function("console.log('Hello Function!')");setTimeout("console.log('Hello setTimeout!')", 1000);

这些操作不能在初始化的解析过程中完成的,也就是说在JavaScript代码运行的同时必须新启动一个解析器来解析新的代码。性能消耗较大。

console.log('Hello Eval!')var sayHello = function() {    console.log('Hello Function!')};setTimeout(function(){    console.log('Hello Function!')}, 1000)

三、发布/订阅

使用jQuery插件https://github.com/cowboy/jquery-tiny-pubsub

jQuery.subscribe("done", f2);function f1(){    // f1执行逻辑    console.log("f1");    setTimeout(function(){        jQuery.publish("done");    }, 1000);}f1();jQuery.unsubscribe("done", f2);     // 取消订阅

这种方法的性质与”事件监听”类似,但是明显优于后者。因为我们可以通过查看”消息中心”,了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。

四、Promises对象

function f1(){    var dfd = $.Deferred();  setTimeout(function () {    // f1的任务代码        console.log("f1");    dfd.resolve();  }, 500);    // 在原来的deferred对象上返回另一个deferred对象,后者只开放与改变执行状态无关的方法(比如done()方法和fail()方法),屏蔽与改变执行状态有关的方法(比如resolve()方法和reject()方法),从而使得执行状态不能被改变。  return dfd.promise();}f1().then(f2);

好处:如果一个任务已经完成,再添加回调函数,该回调函数会立即执行。所以,你不用担心是否错过了某个事件或信号。这种方法的缺点就是编写和理解,都相对比较难。

参考地址:http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html

4 0