jQuery deferred 对象的 promise 方法

jQuery 从 1.5 版本引入了 deferred 对象,这是一个基于 CommonJS Promises/A 的设计,为了方便异步操作,大家都知道,在 js 中,异步代码是非常的多。

这篇博客不是来详细讲解 $.Dererred 的,阮一峰 的博客里有一篇博客详细介绍了这个。但是在这篇博客中,关于 promise() 这个方法的讲解却是错误的,我发现这个是因为我徒弟去学习这个的时候,看了这篇文章,然后我让他给我讲述的时候,发现了这个错误。所以我想写篇 blog 来说明下。

update(2012.1.2):  我针对此问题给一峰发了 email, 他已经针对下面的问题进行了更新,下面的引用来自他最初的版本。现在他博客中对 promise() 的讲解是正确的,如果想了解整个  Deferred 对象,建议直接移步他的博客中学习。同时感谢阮一峰发现的我这篇博客中的一个小错误,最后面的那段代码,现在已经修复。

下面是引用自阮一峰的博客:

这里有两个地方需要注意。

首先,最后一行不能直接返回dtd,必须返回dtd.promise()。原因是jQuery规定,任意一个deferred对象有三种执行状态—-未完成,已完成和已失败。如果直接返回dtd,$.when()的默认执行状态为”已完成”,立即触发后面的done()方法,这就失去回调函数的作用了。dtd.promise()的目的,就是保证目前的执行状态—-也就是”未完成”—-不变,从而确保只有操作完成后,才会触发回调函数。

下面是 jQuery 的官方文档

The deferred.promise() method allows an asynchronous function to prevent other code from interfering with the progress or status of its internal request. The Promise exposes only the Deferred methods needed to attach additional handlers or determine the state (then,donefailalways,pipeprogress, and state), but not ones that change the state (resolverejectprogressresolveWithrejectWith, and progressWith).

也就是说,deferred.promise() 只是阻止其他代码来改变这个 deferred 对象的状态。可以理解成,通过 deferred.promise() 方法返回的 deferred promise 对象,是没有 resolve ,reject, progress , resolveWith, rejectWith , progressWith 这些可以改变状态的方法,你只能使用 done, then ,fail 等方法添加 handler 或者判断状态。

deferred.promise() 改变不了 deferred 对象的状态,作用也不是保证目前的状态不变,它只是保证你不能通过 deferred.promise() 返回的 deferred promise 对象改变 deferred 对象的状态。如果我们这个地方直接返回 dtd,也是可以工作的,.done 的处理函数还是会等到 dtd.resolve() 之后才会执行.

具体在那篇博客的例子, 如果我们把代码改成如下的形式:

var dtd = $.Deferred(); // 新建一个deferred对象
var wait = function(dtd){
    var tasks = function(){
        alert("执行完毕!");
        dtd.resolve(); // 改变deferred对象的执行状态
    };
    setTimeout(tasks,5000);
    return dtd;
};
$.when(wait(dtd))
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });

这样的执行结果和先前返回 dtd.promise 的结果是一样的。

差别在什么地方呢?如果我们把 $.when 的这块的代码改成这样的:

var d = wait(dtd);
$.when(d)
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
d.resolve();

我们会发现 alert(“哈哈,成功了!”) 会立即执行,“执行完毕”却需要5秒后才弹出来。

但是如果我们 wait 函数最后是 return dtd.promise() 这里 d.resolve() 就会报错了,因为对象 d 不存在 resolve() 方法。

同样如果我们把代码改成:

var dtd = $.Deferred(); // 新建一个deferred对象
var wait = function(dtd){
    var tasks = function(){
       alert("执行完毕!");
       dtd.resolve(); // 改变deferred对象的执行状态
   };
   setTimeout(tasks,5000);
   return dtd.promise();
};
dtd.resolve();
$.when( wait(dtd))
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });

我们也可以发现 alert(“哈哈,成功了!”) 会立即执行,因为 dtd 这个 deferred 对象在被传入 wait 之前,已经被 resolve() 了,而 deferred 对象一旦被 resolve 或者 reject 之后,状态是不会改变的。

然后我们再把 $.wait 这块的代码改成:

$.when( wait(dtd))
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
dtd.resolve();

我们也会发现 alert(“哈哈,成功了!”); 被立即执行,虽然 wait(dtd) 执行的时候, dtd 还没有被 resolve,而且 wait 方法返回的是 dtd.promise(), 但是 dtd 这个原始的 deferred 对象是暴露在外面的,我们还是可以从外面改变它的状态。

于是,如果我们真的不想让其他代码能改变 wait 方法内部的 deferred 对象的状态,那我们应该写成这样:

var wait = function(){
    var dtd = $.Deferred(); // 新建一个deferred对象
    var tasks = function(){
        alert("执行完毕!");
       dtd.resolve(); // 改变deferred对象的执行状态
   };
   setTimeout(tasks,5000);
   return dtd.promise();
};
$.when( wait())
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });

也就是不要把 deferred 直接暴露出来,最后返回 deferred.promise() ,让其他地方的代码只能添加 handler 。

UCHOME二次开发,在特定页面的头文件中添加js的方法

昨天找出了添加css文件的方法,今天写js的时候,又阅读了一遍主页面模板,发行在<head>里面没有找到可以添加自定义js的方法,硬要添加的话,就每个页面都要添加,但是我只需要在我开发的这个模块的页面中加载。当然可以把js写在<body>中,但是那样总是不大爽。

没办法,只好从昨天添加css的地方下手,那个是直接echo出的$_SGLOBAL[‘space_css’]这个变量,那我们只要把这个变量写成像这个样子的就可以了:

$_SGLOBAL[‘space_css’]=”@import url(template/default/style_bdc.css);”; //定义CSS文件
$_SGLOBAL[‘space_css’].='</style><script src=”http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js”></script><style type=”text/css”>’;

这应该一看就明白了吧。

我jQuery用的熟一点,所以就打算加载jQuery来写前端代码。这个地方使用的google 的 ajax库CDN加速。

Jquery和javascript对css的操作中css属性名的区别

如果你使用Jquery,操作CSS很简单,可以使用css(name,value)这个方法,而且name都是原生的CSS属性名,使用起来很方便,配合CSS文档就可以了。

但是如果不是用Jquery的话,改变css的属性的时候就要注意了。因为很多属性(例如:background-image)带有‘-’,而“+”和“-”都是JavaScript语言保留给自己使用的特殊字符,不允许用在函数或变量的名字里,所以在方法或属性里面也不可以这样用(方法和属性其实是关联在某个对象上的函数和变量)。DOM解决这个问题的方法是采用“Camel记号”,也就是骆驼命名法,像background-image这个CSS属性就要写成backgroundImage才可以,否则就要出错。

以下两个代码是等价的,一个使用Jquery,一个没使用Jquery,直接使用js。

$(“#test”).css(‘background-image’,’url(“images/test.jpg”)’);

var d=document.getElementById(“test”);
d.style.backgroundImage=’url(“images/test.jpg”)’;

使用Jquery确实是大大的方便了开发,很多东西变的是这么的自然。

JavaScript做DOM操作的时候一定要注意操作的DOM是否已经加载

大家都知道要想操作DOM,要等DOM加载完后才可以操作,我们一般会写window.onload=function(){};

如果使用JQuery,还可以使用

$(function(){
//code
});

这些是已经很熟悉的了,但是如果是个动态更新的DOM呢?这个时候要注意了,绑定事件和操作DOM都要等到这点DOM加载到整个DOM树中。

比如我今晚做的一个弹出层,背景变暗,弹出的层是通过XHR获得的,而且这个层里面我还需要绑定事件,还需要操作DOM,开始我犯傻的就直接和其他时间绑定写在一起了,试了一下没有反应,但是firebug也没报错。监视了一下,发现时间没绑定上。于是恍然大悟,因为我绑定的时候,这点DOM还没加载过来,当然就不去起作用了。

那应该把这个绑定时间写在上面位置呢?因为我用的是XHR获得那个层的内容,并且使用了jQuery,用$.ajax( success: function(){})来判断是否XHR成功,然后在这个function里面写入把获取到的内容加入DOM树的语句,紧接着就可以写事件绑定代码了。这样写,就可以保证是在那片DOM加载成功后再绑定事件了。可以成功的操作了。

Jquery学习笔记-获取select标签已经选中的值

书上介绍有Jquery的form插件,这个插件有个函数:

fieldValue(excludeUnsuccessful)

收集包装集里所有成功表单的值,并且返回这些值所构成的字符串数组。如果未找到任何值,就返回一个空数组。

参数 excludeUnsuccessful(布尔型)如果省略(或设置为true),则指定包装集里任何不成功控件被忽略。

返回 :已收集的值所构成的字符串数组

如果你要处理很多表单,就是用这个form插件吧,但是如果只是处理很少的form,不想使用这个插件呢?

那就用下面的这两种吧:

$('#testSelect option:selected').text();
$("#testSelect").find('option:selected').text();

这样就可以获取到值了。

Jquery中$("#id")和document.getElementById("id")的区别

刚开始学习Jquery。遇到了一些困难。

我使用Jquery的$(“#id”)选择了<select>标签,然后用DOM的add方法插入<option>节点,失败,换用document.getElementByID就可以。一直以为Jquery的$方法返回的是DOM对象,搜索了一下才知道,原来是有差别的,看来是我看书的时候不认真造成的。

用Jquery选择的包装集返回的是Jquery对象,用document.getElementByID返回的却是DOM对象,既然对象不同,方法也就不能混用,所以我开始的时候使用add方法是没用的。这里有一篇文章详细讲解Jquery对象和DOM对象之间的转换。简单说来就是:Query对象是一个数据对象,可以通过[index]的方法,来得到相应的DOM对象。 对于已经是一个DOM对象,只需要用$()把DOM对象包装起来,就可以获得一个jQuery对象了。$(DOM对象)。

所以对于我遇到的问题,我只要使用$(“#id”)[0];就可以获得DOM对象了,就可以使用add方法了。

———————————update———————————————

用Jquery的时候,用.each()可以遍历包装集里面的DOM对象