使用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解耦。




郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。