使用jQuery的Promise
Promise在javascript中得到了越来越广泛的应用,尤其是server side的javascript,比如Node.js编程里。尽管Promise也有其自身的问题,但是它在解决多层嵌套callback,以及其它一些问题时还是有它独特的优点的。
Promise的常用场景基本都是在server side, 但其实在client side, 它也有用武之地,只是目前较为少见而已。
jQuery作为一个优秀的client side的javascript库, 已经全面支持Promise, 只不过我们通常用到的API, 例如$.ajax等,已经把Promise的功能包装在内,开发者感觉不到而已。但是在某些场景下,需要显示的使用Promise来达到目的。
比如这个简单例子:调用外部REST API取得一个任务列表,然后在页面上生成select。
如果想把取得任务列表作为单独一个function,而且不想把生成select的code放到取任务列表的ajax调用的callback里面去, 通常我们会在ajax调用里面使用"async: false",这样一来,取任务列表就变成了同步的。
function generateTaskSelect(value) { var combine = $('<div></div>'); var label = $('<span></span>').text('Task List'); var cr = $('<div></div>'); var select = $('<select></select>'); var names = getTaskNames(); names.sort(); for (var i=0;i<=names.length-1;i++) { select.append(generateOption(names[i])); } if ((value) && (names.indexOf(value) !== -1)) { select.val(value); } else { if (names.length > 0) { select.val(names[0]); } } combine.append(label).append(cr).append(select); return combine; } function getTaskNames() { var names = new Array(); $.ajax({ url: $('#serviceUrl').val() + '/tasks', type: "GET", async: false }).done(function(tasks) { tasks.forEach(function(task){ names.push(task.name); }); }).fail(function(err) { console.error(err); }); return names; } function generateOption(option) { if (option) { return $('<option>' + option + '</option>'); } else { return $('<option></option>'); } }
这个solution在chrome下工作的很好,即使把ajax调用变成了同步的,由于耗时很少,页面感觉不到迟滞。但是它在Firefox下就根本不工作了,原因是Firefix下不支持"async: false"这个属性,所以必须想别的解决办法。这个时候Promise就粉墨登场了。先来看看code。
function generateTaskSelect(value) { var combine = $('<div></div>'); var label = $('<span></span>').text('Task List'); var cr = $('<div></div>'); var select = $('<select></select>'); $.when( getTaskNames() ).done(function(taskNames){ var names = taskNames; names.sort(); for (var i=0;i<=names.length-1;i++) { select.append(generateOption(names[i])); } if ((value) && (names.indexOf(value) !== -1)) { select.val(value); } else { if (names.length > 0) { select.val(names[0]); } } combine.append(label).append(cr).append(select); }).fail(function(err){ console(err); }); return combine; } function getTaskNames() { var dtd = $.Deferred(); $.ajax({ url: $('#serviceUrl').val() + '/tasks', type: "GET" }).done(function(tasks) { var names = new Array(); tasks.forEach(function(task){ names.push(task.name); }); dtd.resolve(names); }).fail(function(err) { console.error(err); dtd.reject(err); }); return dtd; } function generateOption(option) { if (option) { return $('<option>' + option + '</option>'); } else { return $('<option></option>'); } }
使用Promise的新solution里面, 取任务列表现在是异步调用了,而这个异步调用的结果通过Promise可以通知到调用链上层,这样一来不仅达到了避免callback嵌套的目的,而且保持了异步调用,避免占用UI线程,保证了GUI的响应。
其中关键的Promise使用有两点:
1. $.Deferred(): 这就是jQuery中的Promise对象, 注意它的 resolve() 和 reject()状态转换方法的使用。
2. $.when(): 通过它形成了Promise调用链, 对嵌套callback解耦。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。