zoukankan      html  css  js  c++  java
  • javascript中的promise和deferred:实践(二)

    介绍:

    在第一节呢,我花了大量的时间来介绍promises和deferreds的理论。现在呢,我们来看看jquery中的promises(作者一会儿用单数,一会儿用复数形式,妹的)

    Note:代码示例将使用jQuery,尽管它偏离了Promise/A 协议。

    排序模式:

    deferred就是一个未完成的对象,promise呢则是一个未知的值。换句话说,prmises/deferreds 允许我们描述(represent)简单的任务,可以很容易地组合来描述复杂的任务和任务流,允许我们细粒度地控制排序。这就意味着我们可以像写同步代码一样去写异步代码,so easy,妈妈再也不用担心我的学习了。此外,promises让复杂的异步任务变得更容易去抽象成一些小块的功能--比如动画加载,动画处理等等。

    让我们来看看三种常见的排序模式,promises使之变成了可能:堆放,并行和顺序。

    • 堆放:同一promise 事件绑定多个处理
      var request = $.ajax(url);
      
        request.done(function () {
            console.log('Request completed');
        });
      
        // Somewhere else in the application
        request.done(function (retrievedData) {
            $('#contentPlaceholder').html(retrievedData);
        });
    • 并行:请求多个promise,返回一个promise,该promise可以给出执行多个promise的完成情况。
      $.when(taskOne, taskTwo).done(function () {
            console.log('taskOne and taskTwo are finished');
        });
    • 顺序任务:按顺序执行任务。
      var step1, step2, url;
      
      url = 'http://fiddle.jshell.net';
      
        step1 = $.ajax(url);
      
        step2 = step1.then(
          function (data) {
              var def = new $.Deferred();
      
              setTimeout(function () {
                  console.log('Request completed');
                  def.resolve();
              },2000);
      
            return def.promise();
      
        },
          function (err) {
              console.log('Step1 failed: Ajax request');
          }
        );
        step2.done(function () {
            console.log('Sequence completed')
            setTimeout("console.log('end')",1000);
        });

    这些模式可以组合使用或者单独使用,用以建立复杂的任务或工作流。

    常见示例:

    许多的promise示例都使用Ajax请求和UI动画。实际上,jQuery的Ajax请求默认返回的是个promise。这给人造成一种错觉,以为解决异步任务完美解决方案就是promise了。其实不然,promise就是一个值得你在任何时候去考虑使用的工具,而不仅仅是回调。让我们看看使用promise的实例吧。

    • ajax,这个就算了,一搜一大把,我就略过啦。
    • 定时:创建一个基于timeout的函数:
      function wait(ms) {
            var deferred = $.Deferred();
            setTimeout(deferred.resolve, ms);
      
           // We just need to return the promise not the whole deferred.
           return deferred.promise();
        }
      
        // Use it
        wait(1500).then(function () {
            // Do something brilliant here!
        });
    • 动画:显然下面的动画是没啥实际用处的,但是这个示例却给出了promise和动画如何一起使用。
      var fadeIn = function (el) {
      
            var promise = $(el).animate({
                opacity: 1
            }, 1500);
      
            // Dynamically create and return an observable promise object which will be resolved when the animation completes.
           return promise.promise();
        };
      
      var fadeOut = function(el) {
      
          var promise = $(el).animate({
              opacity: 0
          }, 1500);
      
          // Dynamically create and return an observable promise object
            return promise.promise();
      };
      
      // With the setup out of the way, we can now do one of the following.
      
      // Parallel
      $.when(
          fadeOut('div'), 
          fadeIn('div')
      ).done(function () {
          console.log('Animation finished');
          $('p').css('color', 'red');
      });
      
      // OR
      // Chained
      fadeOut('div').then(function (el) {
          fadeIn(el); // returns a promise
      }).then(function (el) {
          fadeOut(el); // returns a promise
      });
    • 使用$.when()同步并行任务
      var promiseOne, promiseTwo, handleSuccess, handleFailure;
            
              // Promises
              promiseOne = $.ajax({ url: '../test.html' });
              promiseTwo = $.ajax({ url: '../test.html' });
                
                
              // Success callbacks
              // .done() will only run if the promise is successfully resolved
              promiseOne.done(function () {
                  console.log('PromiseOne Done');
              });
              
              promiseTwo.done(function () {
                  console.log('PromiseTwo Done');
              });
            
              // $.when() creates a new promise which will be:
              // resolved if both promises inside are resolved
              // rejected if one of the promises fails
              $.when(
                  promiseOne,
                  promiseTwo
              )
              .done(function () {
                  console.log('promiseOne and promiseTwo are done');
              })
              .fail(function () {
                  console.log('One of our promises failed');
              });
    • 解耦事件和程序逻辑(jsfiddle demo
      var def, getData, updateUI, resolvePromise;
      
      // The Promise and handler
      def = new $.Deferred();
      
      updateUI = function (data) {
          $('p').html('I got the data!');
          $('div').html(data);
      };
      getData = $.ajax({
                url: '/echo/html/', 
                data: {
                    html: 'testhtml', 
                    delay: 3
                }, 
                type: 'post'
          })
          .done(function(resp) {
              return resp;
          })
          .fail(function (error) {
              throw new Error("Error getting the data");
          });
      
      
      // Event Handler
      resolvePromise = function (ev) {
          ev.preventDefault();
          def.resolve(ev.type, this);
          return def.promise();
      };
      
      // Bind the Event
      $(document).on('click', 'button', resolvePromise);
      
      def.then(function() {
          return getData;   
      })
      .then(function(data) {
          updateUI(data);
      })
      .done(function(promiseValue, el) {
          console.log('The promise was resolved by: ', promiseValue, ' on ', el);
      });
      
      
      // Console output: The promise was resolved by: click on <button> </button> 

    Gotcha’s: 理解jQuery中的.then()

    为了演示一对“gotcha's”,这些最终的实例将会贯穿我的整个promise实践。

    让我们来创建两个公用函数:

    // Utility Functions
    function wait(ms) {
          var deferred = $.Deferred();
          setTimeout(deferred.resolve, ms);
          return deferred.promise();
    }
    function notifyOfProgress(message, promise) {
        console.log(message + promise.state());
    }

    第一次的promise链式写法看起来就像这样:

    // Naive attempt at working with .then()
    
    // Create two new deferred objects
    var aManualDeferred = new $.Deferred(),
        secondManualDeferred = aManualDeferred.then(function () {
            console.log('1 started');
    
            wait(3500).done(function () {
                console.log('1 ended');
            });
        });
    
    // After secondManualDeferred is resolved
    secondManualDeferred.then(function () {
        console.log('2 started');
    
        wait(2500).done(function () {
            console.log('2 ended');
        });
    });
    
    // Resolve the first promise
    aManualDeferred.resolve();

    执行的输出结果:

    1 started
    2 started
    2 ended
    1 ended

    纳尼?jQuery API不是说 .then()方法可以链式并返回promise么?我所期望的是无论我在.then()方法中插入了任何代码,程序都应顺序执行,只有上一个任务完成了,才可以执行下一个。但是这很明显不是我所期望的结果啊?为毛啊?

    .then()原理

    查看jQuery 源代码,我们可以发现:

    • .then()方法始终返回一个新的promise
    • .then()必须传递一个函数作为回调

    如果.then()没有传递函数,那么:

    • 新的promise将会拥有和初始promise行为一致(这就意味着它立即 被解决/被拒绝)
    • .then()中的输入将被执行但是会被.then()忽略

    如果.then()被传递了一个函数,该函数返回了一个promise 对象,那么:

    • 新的promise将和返回的promise行为一致
      var deferred = $.Deferred(),
              secondDeferred = deferred.then(function () {
                return $.Deferred(function (newDeferred) {
                  setTimeout(function() {
                    console.log('timeout complete');
                  newDeferred.resolve();
                }, 3000);
              });
            }),
            thirdDeferred = secondDeferred.then(function () {
                console.log('thirdDeferred');
            });
      
          secondDeferred.done(function () {
              console.log('secondDeferred.done');
          });
          deferred.resolve();
    • 如果.then()传递的是一个函数,该函数返回一个值,那么这个值将成为新对象的值
      var deferred = $.Deferred(),
              filteredValue = deferred.then(function (value) {
                return value * value;
              });
      
          filteredValue.done(function (value) {
              console.log(value);
          });
      
          deferred.resolve(2); // 4

    你可能已经注意到了,为毛我的版本无法运行(可是我能运行啊,作者,你肿么了)。我没有立即从.then()返回一个promise,所以由.then()创建的新的promise拥有同样的值。

    避免被回调坑爹(Avoiding the descent into callback hell)

    我们知道.then()需要一个回调函数并返回一个promise,所以我们可以像下面这么做:

    // Anti-pattern - Return to callback hell
    
    var aManualDeferred = new $.Deferred();
    
    aManualDeferred.then(function () {
        console.log('1 started');
    
        return wait(3500).then(function () {
            console.log('1 ended');
        }).then(function () {
            console.log('2 started');
    
            return wait(2500).done(function () {
                console.log('2 ended');
            });
        });
    });
    
    // Resolve the first promise
    aManualDeferred.resolve();

    运行啦。不幸的是,这个回调太shit了,我们又掉到回调嵌套的坑里了。还好,我们有绝招来规避这种嵌套。那么,如何解决这个问题呢,当然,这得具体情况具体分析咯。

    避免使用大量的无名promise

    举例如下:

    // A chain
    // Create new deferred objects
    var aManualDeferred = $.Deferred();
    
    aManualDeferred.then(function () {
        console.log('1 started');
    
        // We need to return this, we return a new promise which is resolved upon completion.
        return wait(3500);
    })
    
    .then(function () {
        console.log('1 ended');
    })
    
    .then(function () {
        console.log('2 started');
        return wait(2500);
    })
    
    .then(function () {
        console.log('2 ended');
    });
    
    // Resolve the first promise
    aManualDeferred.resolve();

    这次看起来就漂亮多啦。缺点是,只有一个promise是命名的,不利于我们细粒度地控制每个步骤,这个在很多情况下是非常有用的哦。

    解耦promise和处理函数

    假如我们不想深层嵌套,我们就得命名promise,这样我们就有了每个步骤的控制权。

    看看最终版本:

    var aManualDeferred, secondManualDeferred, thirdManualDeferred;
    
    // Create two new deferred objects
    aManualDeferred = $.Deferred();
    
    secondManualDeferred = aManualDeferred.then(function () {
        console.log('1 started');
    
        // We need to return this, we return a new promise which is resolved upon completion.
        return wait(3500);
    })
    .done(function () {
        console.log('1 ended');
    });
    
    thirdManualDeferred = secondManualDeferred.then(function () {
        console.log('2 started');
        return wait(2500);
    })
    .done(function () {
        console.log('2 ended');
    });
    
    // Check current state
    thirdManualDeferred.notify(
        notifyOfProgress('thirdManualDeferred ', thirdManualDeferred)
    );
    
    // Resolve the first promise
    aManualDeferred.resolve();
    
    // Console output
    // aManualDeferred pending
    // secondManualDeferred pending
    // 1 started
    // 1 ended
    // 2 started
    // 2 ended

    这个优点就很明显了,现在的程序分为三个步骤,我们可以访问每个promise的状态,用以发送进程通知,或者在管理代码执行顺序时,也不需要重写代码(谁说的,只不过修改代价很小了)。

    上下文和传值

    在Ajax示例中,我们看到,可以给.resolve()和.fail()函数传值。如果一个promise resolved(我觉得“resolved”这里还是不翻译的好)了一个值,那么新的promise就是返回值本身。

    var passingData = function () {
        var def = new $.Deferred();
    
        setTimeout(function () {
            def.resolve('50');
        }, 2000);
    
       return def.promise();               
    };
    
    passingData().done(function (value) {
          console.log(value);
    });

    当我们 resolve 了一个promise,我们可以给它设置 this。

    // Create an object
    var myObject = {
        myMethod: function (myString) {
            console.log('myString was passed from', myString);
        }
    };
    
    // Create deferred
    var deferred = $.Deferred();
    
    // deferred.done(doneCallbacks [, doneCallbacks ])
    deferred.done(function (method, string) {
        console.log(this); // myObject
    
        // myObject.myMethod(myString);
        this[method](string);
    });
    
    deferred.resolve.call(myObject, 'myMethod', 'the context');
    
    => myString was passed from the context
    
    // We could also do this:
    // deferred.resolveWith(myObject, ['myMethod', 'resolveWith']);
    // but it's somewhat annoying to pass an array of arguments.
    
    // => myString was passed from resolveWith

    剩下的是最佳实践和jquery 中的一些方法介绍,作者一带而过,我就不翻译了。想看,就看原文吧。 

    ps:

    所有的deffered对象都支持的方法:

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

    then(doneCallbacks,failCallbacks):由此可知,then可以处理成功和失败的回调,这就是和done的区别啊

    done(doneCallbacks)

    fail(failCallbacks)

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

    ajax对象还包括

    sucess

    fail

    它们会分别映射到deffered对象的done 和fail方法上

    译自:http://blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt2-practical-use

  • 相关阅读:
    安卓手机数据库和自动寻路插件
    快速找图找字插件
    大龙快速找图,找字插件
    C星【多线程】寻路插件最新更新2019-8-17
    大龙数据库插件,通用所有数据库,支持任何语言调用
    开源一个自己造的轮子:基于图的任务流引擎GraphScheduleEngine
    Java中使用CountDownLatch进行多线程同步
    TensorFlow实验环境搭建
    Java中Semaphore(信号量)的使用
    CNN在中文文本分类的应用
  • 原文地址:https://www.cnblogs.com/my_front_research/p/3228333.html
Copyright © 2011-2022 走看看