我们先来看一下编写AJAX编码经常遇到的几个问题:

1.由于AJAX是异步的,所有依赖AJAX返回结果的代码必需写在AJAX回调函数中。这就不可避免地形成了嵌套,ajax等异步操作越多,嵌套层次就会越深,代码可读性就会越差。

$.ajax({
url: url,
data: dataObject,
success: function(){
console.log("I depend on ajax result.");
},
error: function(){}
}); console.log("I will print before ajax finished.");

2.如果AJAX请求之间存在依赖关系,我们的代码就会形成Pyramid of Doom(金字塔厄运)。比如我们要完成这样一件事:有4个供Ajax访问的url地址,需要先Ajax访问第1个,在第1个访问完成后,用拿到的返回数据作为参数再访问第2个,第2个访问完成后再第3个...以此到4个全部访问完成。按照这样的写法,似乎会变成这样:

$.ajax({
url: url1,
success: function(data){
$.ajax({
url: url2,
data: data,
success: function(data){
$.ajax({
//...
});
}
});
}
});

3.考虑这种场景,假如我们同时发送两个Ajax请求,然后要在两个请求都成功返回后再做一件接下来的事,想一想如果只按前面的方式在各自的调用位置去附加回调,这是不是很困难?

可以看到:JavaScript中类似于AJAX这种异步的操作,会导致代码嵌套层次复杂,可读性差,有的时候甚至是实现需求都非常困难。为了解决这种异步回调难的问题,CommonJS组织制定了异步模式编程规范Promises/A。目前该规范已经有了很多的实现者,比如Q, when.js, jQuery.Deffered()等。我们以jQuery.Deffered学习下Promise。

Promise的状态

Promise对象有3种可能的状态:肯定状态(resolved)、否定状态(rejected)、等待状态(pending)。刚开始创建的Promise对象处于pending状态,只能从pending变成resolved或者是从pending变成rejected状态。

var df1 = $.Deferred();
console.log(df1.state());//pending var df2 = $.Deferred();
df2.resolve();//resolved
console.log(df2.state()); var df3 = $.Deferred();
df3.reject();
console.log(df3.state());//rejected

$.Deferred()创建一个延迟对象(也就是Promise对象),deferred.state()可以获取Promise对象当前所处的状态。deferred.resolve()和deferred.reject()则是用来改变Promise对象的状态。

Promise添加回调函数

Promise对象有3种状态,我们可以分别为这3种状态注册回调函数。当Promise处于某个状态的时候,会触发这个状态下注册的回调函数。

var df = $.Deferred();
df.done(function(){alert("success");});
df.fail(function(){alert("fail");});
df.progress(function(){alert("progress");}); df.notify(); df.resolve();
// df.reject();

done()、fail()、progress()分别注册resolved、rejected、pending状态下的回调函数。通过resolve()、reject()、notify()可以触发事先注册的回调函数。

Promise是支持链式调用的,上面的代码可以写成下面的样子。

var df = $.Deferred();
df.done(function(){alert("success");})
.fail(function(){alert("fail");})
.progress(function(){alert("progress");});

Promise支持多个回调函数,会按照注册顺序调用。

var df = $.Deferred();
df.done(function(){alert("first");})
.fail(function(){alert("fail");}); df.done(function(){alert("second");});
df.done(function(){alert("third");}); df.resolve();

deferred.always()添加的回调函数,无论Promise是resolved状态还是rejected状态,都会被调用。

var df1 = $.Deferred();
df1.always(function(type){alert(type);});
df1.resolve("resolve"); var df2 = $.Deferred();
df2.always(function(type){alert(type);});
df2.reject("reject");

progress()和notify()能够用来实现进度条效果,因为notify()允许调用多次,而reject()和resolve()只能调用一次。这个很好理解,因为一旦状态变成resolved或者是rejected,就不能再改变其状态,也没有必要。

var df = $.Deferred();
df.done(function(){alert("success");});
df.fail(function(){alert("fail");});
df.progress(function(){alert("progress");}); // resolve()调用2次,但是只能触发1次success
df.resolve();
df.resolve(); var mudf = $.Deferred();
mudf.done(function(){alert("success");});
mudf.fail(function(){alert("fail");});
mudf.progress(function(){alert("progress");}); // 每次调用notify都会触发progress回调函数
mudf.notify("%10");
mudf.notify("%20");

rejectWith()、resolveWith()、notifyWith()功能上和reject()、resolve()、notify()没有什么差别,主要差别在于回调函数中的执行上下文(方法中的this)和参数形式。具体差别可以参考"JQuery.Callbacks系列一:api使用

// 老的ajax写法
$.ajax({
  url: "test.html",
  success: function(){
    alert("success");
  },
  error:function(){
    alert("error");
  }
}); // 使用promise后的写法
$.ajax("test.html")
.done(function(){})
.fail(function(){})
.done(function(){)
.fail(function(){);

JQuery中的Deferred对象与Promise对象区别

JQuery.Deferred相关的API,有的返回的是Deferred对象,有的返回的是Promise对象。如done()、reject()等大部分函数返回的都是Deferred对象,$.when()和then()函数返回的是Promise对象。具体可以参考JQuery API文档。

JQuery官方对Promise Objects的解释是:

This object provides a subset of the methods of the Deferred object (then, done, fail, always, progress, state and promise) to prevent users from changing the state of the Deferred.

可以看到Promise对象其实就是Deferred对象的一部分,Deferred对象提供了notify、reject、resolve等改变状态的方法,但是Promise对象没有提供这些方法。

文章开始提到的AJAX问题1~3,问题1可以很容易通过Promise得到解决。问题2和问题3是通过$.when()和deferred.then()得到解决,由于这2个API相对来说复杂一些,以后的文章再分析这2个API。

详解"这篇文章中的fire()和fireWith()。

上面简单的介绍了Promise的使用方式,我们可以用Promise的方式来编写AJAX代码。可以很容易地看出:使用Promise后代码嵌套层次少了,代码是纵向增长的,而不再是横向增长。而且使用Promise,可以指定多个ajax回调函数。

 
 
jquery Deferred 快速解决异步回调的问题
 
function ok(name){

  var dfd = new $.Deferred();
callback:func(){ return dfd.resolve( response );
} return dfd.promise();
} $.when(ok(1),ok(2)).then(function(resp1,resp2){})

//相关API 分成3类

1类:$.when(pro1,pro1) 将多个 promise 对象以and的关系 合并为1个

2类:promise 激发为 解决 deferred.resolve([ args ] ) deferred.resolveWith( context, [ args ] )

和 拒绝 .reject  .rejectWith

context 上下文 替换 this 和通知 .notify  .notifyWith

3类: 对激发的响应  解决时deferred.done(args) 拒绝时 deferred.fail() 通知时 deferred.progress()

不管 解决 或 拒绝 deferred.always()

deferred.then( doneCallbacks, failCallbacks [, progressCallbacks] )

promise(或者叫deferred 延迟对象如何获取?)

var dfd = new $.Deferred(); return dfd.promise();

返回promise当前状态

deferred.state()  pending(尚未完成) resolved rejected

管道

deferred.pipe( [ doneFilter ], [ failFilter ] ) 

var defer = $.Deferred()

var filtered = defer.pipe( null, function( value ) {

   return value * 3;
}); defer.reject( 6 );
filtered.fail(function( value ) {
alert( "Value is ( 3*6 = ) 18: " + value );
});

jquery.Deferred promise解决异步回调的更多相关文章

  1. js中的回调函数 和promise解决异步操作中的回调地狱问题。

    回调函数 : 函数作为参数传递到另外一个函数中.简单数据类型和引入数据类型中的数组和对象作为参数传递大家肯定都不陌生,其实引用数据类型中的函数也是可以的. 事实上大家见到的很多,用到的也很多,比如jQ ...

  2. js中promise解决callback回调地狱以及使用async+await异步处理的方法

    1.callback回调地狱 function ajax(fn) { setTimeout(()=> { console.log('你好') fn() }, 1000) } ajax(() =& ...

  3. jquery deferred promise

    <script type="text/javascript">/* Deferredstate (then,done, fail, always,pipe, progr ...

  4. 用ES7解决异步回调地狱问题

    用了 Promise 其实并没有真正解决回调地狱问题,并且还新增了很多 .then(data => { .... }) 这些很没有意义的 “模板代码”.所以先人们又搞出了generator 和 ...

  5. 利用Generator解决异步回调原理

    var i = 0; i++; function ajax(url){ return new Promise(function(resolve, reject){ setTimeout(functio ...

  6. Promise对象和回调函数,解决异步数据传递问题

    下面的代码例子,均已小程序的异步请求数据为案例来说明 1.利用回调函数,来解决异步数据传递问题 异步操作api.js const getBooks = (url, callback) => { ...

  7. async + promise 解决回调地狱

    // 解决异步回调地狱的方案: async + promise async function writeFile() {   // 打开文件   const fd = await new Promis ...

  8. js异步回调Async/Await与Promise区别 新学习使用Async/Await

    Promise,我们了解到promise是ES6为解决异步回调而生,避免出现这种回调地狱,那么为何又需要Async/Await呢?你是不是和我一样对Async/Await感兴趣以及想知道如何使用,下面 ...

  9. JS魔法堂:jQuery.Deferred(jQuery1.5-2.1)源码剖析

    一.前言 jQuery.Deferred作为1.5的新特性出现在jQuery上,而jQuery.ajax函数也做了相应的调整.因此我们能如下的使用xhr请求调用,并实现事件处理函数晚绑定. var p ...

随机推荐

  1. 参加2015年TOP100会议的零散笔记

    2015年出差很少,感到整个技术都已经荒废了,收到12月份TOP100的会议通知后,还是去充点电吧,不然心慌啊.对于软件大会这种大杂烩式的会议已经没有多少兴趣了,看看这个TOP100组织得有何不同? ...

  2. 微信html5开发选哪一个

        html5选哪个开发框架       ①. Justep起步 X5框架 : 专门用来开发web App的大型开发工具集,x5 v3.0有765.58M,不选. ②.Jquery Mobile ...

  3. 关于Fragment 不响应onActivityResult的情况分析 (

    大家都知道,可以通过使用 startActivityForResult() 和 onActivityResult() 方法来传递或接收参数. 但你是否遭遇过onActivityResult()不执行或 ...

  4. 安卓开发_浅谈Android动画(二)

    在学习了四个基本动画之后,现在要学习一些更有用的效果 先给出所有的动画xml <?xml version="1.0" encoding="utf-8"?& ...

  5. Spring(八)SSH整合简述

    一.Spring与Struts2的整合 1.1.整合步骤 1.2.配置web.xml 1.3.配置spring配置文件applicationContext.xml 1.4.配置struts配置文件 1 ...

  6. android学习笔记 对话框合集

    package com.zhangbz.dialog; import android.app.Activity; import android.app.AlertDialog; import andr ...

  7. dig与dns基本理论——解析和缓存

    DNS(Domain Name System,域名系统)也许是我们在网络中最常用到的服务,它把容易记住的域名,如 www.google.com 翻译成人类不易记住的IP地址,如 173.194.127 ...

  8. jquery错误: Cannot read property ‘msie’ of undefined

    背景 Web application, 引用了jquery 1.10.2和fancybox 1.3.4 现象 访问页面遭遇Cannot read property ‘msie’ of undefine ...

  9. Effective Java 28 Use bounded wildcards to increase API flexibility

    Get and Put Principle PECS stands for producer-extends(? extends T), consumer-super(? super T). For ...

  10. Effective Java 35 Prefer annotations to naming patterns

    Disadvantages of naming patterns Typographical errors may result in silent failures. There is no way ...