Deferred跟promise跟js同步问题记录

  之前的时候,碰到过几次同事问我,说js的同步怎么处理,就是我想先执行这段代码(耗时相对较长的一行,多数是异步的一些api调用),执行完了之后我再执行下边这句,每次我都很无奈的说不晓得,如果是ajax的话我知道async可以搞定,当然这并没有什么乱用。近段时间在做一个自动化测试的项目,前后端分离的。旁边的同事要做前端websql的增删改查,一直在说jquery的deferred如何如何,我也不懂,就感觉听上去高大上的一个东西,内心忐忑之余就找两篇文章读了一下,然后就有了此文。

------------------------------------------------------------------------------------------------------------------------------------------------------------

  js如何执行的问题:js不是严格一行一行的执行的,它是一段一段的进行解析,这个解析过程主要是对声明的提前,剩余的基本上还是按照先后顺序执行的。js是单线程的,正常情况下的确是先执行上一行然后下一行的,那很多异步的api是怎么实现的呢,执行队列。关于这里可以参考这篇文章:http://www.cnblogs.com/3body/p/5691744.html,关于同步的问题怎么实现呢,依然不好搞,但可以通过回调函数让它看起来是同步的。 

在回调函数方面,jquery的功能比较弱,为了改变这一点,jquery团队设计了deferred对象。简单来说,deferred对象就是jquery的回调函数解决方案。在英语中,defer的意思是“延迟”,所以deferred对象的含义就是延迟到未来某个时间点再来执行。Deferred对象是jquery从1.5.0开始引入的,它彻底改变了如何在jquery中使用ajax。为了实现它,jquery的全部ajax代码都被改写了。
jquery1.5之前的版本,ajax返回的是XHR(xmlHttpRequest)对象,可以从jquery的api中看到:

从1.5开始,返回的还是xhr对象,但是这个对象实现了deferred的接口方法,所以可以当成deferred使用,也因此,jquery的可以有了deferred版本的写法:
原来我们的ajax写法: 

$.ajax({
    url: "test.html",
    success: function(){
        alert("哈哈,成功了!");
    },
    error:function(){
        alert("出错啦!");
    }
});

  如果是1.5之后的jquery,可以这么写:

$.ajax("test.html")
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });

 deferred对象究竟长啥样子,我们可以看图:

 

  这里边有几个常用方法,resolve()表示将deferred的状态置为已解决也就是成功,reject()表示把状态置为失败,done()是执行成功的回调函数,fail是失败的回调函数,always()是无论如何都会执行的函数,类似于try...catch的finally,progress()是状态变为开始执行的回调函数,state()可以获取deferred对象的状态。
  如何使用这个deferred对象呢?

  1、其实前面的代码中已经使用了,$.ajax(url).done(function(){}).fail(function(){})这行代码中,$.ajax()返回了一个deferred对象,然后我们链式的调用,使用了这个对象的done跟fail方法来设定了两个回调函数;
  2、如果我要判断多个方法执行完成后再调用某个函数,怎么做呢?
   这里要用到$.when()

$.when($.ajax("test1.html"), $.ajax("test2.html"))
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });

  3、如果我不是ajax呢,自己手写的函数怎么做呢,让函数返回一个deferred对象即可:

function a(){
    var d1 = $.Deferred();
    d1.resolve("aa","bb",{"myaa":"xxxx"});
    return d1;
}

function b(){
    var d2 = $.Deferred();
    d2.reject();
    return d2;
}

$.when(a(),b())
.done(function(a,b,c){debugger;alert("success"+a);})
.fail(function(){alert("error");})

  顺便我们可以看到,deferred对象可以传参!只是要注意的是resolve方法里边的参数不管有几个,最后会以一个数组的形式传给回调函数,回调函数只有一个接收参数。上述代码的执行结果如下:

  

  可以看到只有a有值,其它两个参数都在回调函数第一个参数的两个数组元素中。

  这里有个要注意的地方,就是$.when()只接收deferred对象,如果你传给when的不是deferred对象,会发现函数会马上执行的,起不到回调的作用;
  我们很容易发现一个问题,deferred对象的状态是可以手动代码修改的,因为resolve()就可以置为成功,reject()就可以置为失败,在复杂代码环境下这个太危险了。因此有了promise对象。
  我本地用的jquery-1.11.3,看上面deferred的结构图可以看到有个方法叫promise(),这个就是返回一个pormise对象。这个pormise对象跟deferred是非常类似的,差别就是promise对象去掉了reject()跟resolve()方法,也就是不对外提供修改状态的方法,免得多方调用引起混乱。其实jquery返回的就是个promise对象,我们想改状态是改不了的。看一下ajax返回对象跟pormise的结构图:

  

          图:ajax  XHR

  

          图:promise

  我们可以很清晰的看到这两个promise比deferred少了resolve,resolveWith,reject,rejectWith等这些改变对象状态的方法。promise不希望我们能手动更改promise对象的状态。因为:假设场景中,我们返回promise对象给它人使用,这个对象被a跟b两个方法引用了,a方法改变了promise的状态,那么这会对b方法造成影响,这是很不安全的。

  那么,deferred跟promise就是为了帮助我们实现同步编程的吗?
  NO!恰恰相反,它是为了帮我们实现异步编程的!有时候我们在浏览器端会碰到这样的需求,既要保持页面与用户的交互不受影响,又要协调页面与异步任务的关系。看如下的代码是否存在问题:

function test(){
	//一些业务操作
	var var1 = ....
	var var2 = ....
	//后台交互
	$.ajax({
		url:myurl,
		data:dataObj,
		async:false,
		success:function(result){
			//成功回调函数
			successFun(result);
		},
		error:function(){
			//失败处理函数
			failFun();
		}
	})

	//一些其它的业务操作
	functionOperate(...);
} 

  这段代码其实是有问题的,因为ajax被设定为同步的,如果后台一直没返回结果,这时候functionOperate()方法是不能执行的,也就是实际上浏览器会僵死掉,这是极其影响用户体验的;还有就是如果successFun或者failFun中仍有需要ajax跟后台交互的内容,多个ajax形成代码嵌套,是相对较难理解的代码书写方式。
  该怎么做呢?deferred(或者promise)的方式来做:

function test(){
	//一些业务操作
	var var1 = ....
	var var2 = ....
	//后台交互
	$.when($.ajax({url:myurl,data:dataObj})).done(successFun(result)).fail(failFun());
	//一些其它的业务操作
	functionOperate(...);
}

  把多个回调函数写成独立的function,然后用$.when的方式进行调用,看起来思路相对清晰;关键是这么写不会阻塞functionOperate方法的执行!也就是浏览器不会死掉,用户体验要比第一种好的多。

------------------------------------------------------------------------------------------------------------------------------------------------------------

  额,就这些了,看了个大概,不知有没有错误的地方,,,请指出改正。

时间: 2024-07-28 15:42:50

Deferred跟promise跟js同步问题记录的相关文章

使用 jQuery Deferred 和 Promise 创建响应式应用程序

这篇文章,我们一起探索一下 JavaScript 中的 Deferred 和 Promise 的概念,它们是 JavaScript 工具包(如Dojo和MochiKit)中非常重要的一个功能,最近也首次亮相于 流行的 JavaScript 库 jQuery(已经是1.5版本的事情了). Deferred 提供了一个抽象的非阻塞的解决方案(如 Ajax 请求的响应),它创建一个 “promise” 对象,其目的是在未来某个时间点返回一个响应.如果您之前没有接触过 “promise”,我们将会在下面

2017、2018面试分享(js面试题记录)记得点赞分享哦;让更多的人看到~~

2017面试分享(js面试题记录) 1. 最简单的一道题 '11' * 2 'a8' * 3 var a = 2, b = 3; var c = a+++b; // c = 5 2. 一道this的问题 var num = 10; var obj = { num:8, inner: { num: 6, print: function () { console.log(this.num); } } } num = 888; obj.inner.print(); // 6 var fn = obj.

JS 同步输入

var txtBigBalance; //金额同步输入 if ($.browser.msie)//IE { $("#txtBalanceP").get(0).onpropertychange = getBalance; $("#txtbigBalance").get(0).onpropertychange = eventHandler; } else { var intervalName; $("#txtBalanceP").get(0).add

js同步访问后台资源

$.ajax( {  type : 'post',  url : url,  data : data,  async : false,//false代表只有在等待ajax执行完毕后才执行window.open('http://www.daimajiayuan.com')语句   success : function(data) {   var evalData = eval("(" + data + ")");   if (evalData[0].resultCod

js同步-异步-回调

出处:https://blog.csdn.net/u010297791/article/details/71158212(1)上面主要讲了同步和回调执行顺序的问题,接着我就举一个包含同步.异步.回调的例子. let a = new Promise(//声明了一个Promise回调函数,能够使用then function(resolve, reject) { console.log(1) setTimeout(() => console.log(2), 0) console.log(3) cons

JS继续学习记录(一)

JS继续学习记录(一) 总感觉自己的js code写的还算可以,但是又深知好像只知道一些皮毛,所以打算仔细记录一下js晋级学习过程,日日往复 先记录一下自己目前对js的了解吧(20180828) js作为脚本语言,包括核心语法,dom,bom三部分组成. 1.核心语法:变量,函数的定义,一些js内置对象的调用,如array,如json,基本数据类型,引用数据类型若干,各自的用法云云. 2.dom对象:html的页面对象的封装,封装成的dom对象内包括html的基础属性,衍生属性,监听器,绑定函数

Deferred解决JS同步问题

测试脚本: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <

Node.js之Promise维护(同步)多个回调(异步)状态

金天:学习一个新东西,就要持有拥抱的心态,如果固守在自己先前的概念体系,就会有举步维艰的感觉..NET程序员初用node.js最需要适应的就是异步开发, 全是异步,常规逻辑下遍历列表都是异步,如何保证列表遍历执行完毕?Promise帮你搞定!金天微信:15998603918 欢迎找我聊聊天. Node.js编程,清一色的回调. 如果没有Promise, 一连串的业务逻辑,从第一步回调到最后一步,“单线程逻辑”还搞的定,遭遇“多线程逻辑”,便陷入回调地狱. 自从有了Promise, 一切都不在是问

centos 6.5设置mysql主从同步过程记录

在centos 6.5上设置了mysql主从功能,记录一下. 服务器1(主)IP:192.168.137.144系统版本:centos 6.5mysql版本:mysql 5.5 服务器2(从)IP:192.168.137.185系统版本:centos 6.5mysql版本:mysql 5.5 这里两台服务器的系统版本和mysql版本均一致,这也是官方推荐的做法.在开始设定之前,最好能确保主库和从库一致. 1.主库和从库创建同步用户 mysql> grant replication slave,