• javascript异步代码的回调地狱以及JQuery.deferred提供的promise解决方式


    我们先来看一下编写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使用具体解释"这篇文章中的fire()和fireWith()。


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

    // 老的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能够非常easy通过Promise得到解决。问题2和问题3是通过$.when()和deferred.then()得到解决。因为这2个API相对来说复杂一些,以后的文章再分析这2个API。


    參考文章

    "异步JavaScript与Promise"作者ACGTOFE



  • 相关阅读:
    View Controller 生命周期的各个方法的用法
    IOS开发之Post 方式获取服务器数据
    委托代理
    Function
    SKPhysicsContactDelegate协议
    UITouch附加
    Remove Duplicates from Sorted Array II
    4Sum
    [Text Justification
    Count and Say
  • 原文地址:https://www.cnblogs.com/lytwajue/p/6747713.html
Copyright © 2020-2023  润新知