2015-06-10 15 views
5

Non riesco a trovare la funzione delay o wait per le promesse di jQuery. Ho trovato una funzione sul SO (Using jQuery.Deferred to avoid nested setTimeout callbacks):ritardo per jquery promises

function delay(time) { 
    return function() { 
     console.log("Delaying"); 
     var ret = new $.Deferred(); 
     setTimeout(function() { 
      ret.resolve(); 
     }, time); 
     return ret; 
    }; 
} 

E, è il modo in cui lo uso:

run: function() { 
     return $() 
      .promise() 
      .then(function() { 
       console.log("call together"); 
       console.log("call together");  
      }) 
      .then(delay(2000)) 
      .then(function() { 
       console.log("call first"); 
      }) 
      .then(delay(2000)) 
      .then(function() { 
       console.log("call second"); 
      }) 
    } 

voglio estendere promessa o un oggetto in differita che posso scrivere come:

run: function() { 
      return $() 
       .promise() 
       .then(function() { 
        console.log("call together"); 
        console.log("call together");  
       }) 
       .delay(2000) 
       .then(function() { 
        console.log("call first"); 
       }) 
       .delay(2000) 
       .then(function() { 
        console.log("call second"); 
       }) 
     } 
+0

@ guest271314: Ho bisogno funzione proprio per promesse. Questa funzione per effetti. – user348173

+0

Possibile duplicato di [È possibile aggiungere metodi all'oggetto promessa di JQuery?] (Http://stackoverflow.com/q/30719454/1048572) (basta aggiungere un metodo '.delay' invece di' .catch') – Bergi

+0

@ jfriend00: Beh, certo [è possibile] (http://stackoverflow.com/a/30719727/1048572) ... ma non così facilmente come lo è l'estensione di un prototipo. – Bergi

risposta

3

Come @Bergi dice jQuery Deferreds/Le promesse non sono estendibili per eredità prototipale.

Invece, il modello adottato dalla jQuery è quello di consentire istanze promessa individuali necessari per essere estesi con la sintassi:

deferred.promise(target); 
//or, 
promise.promise(target); //(though the documentation doesn't make this clear) 
// where `target` is an "object onto which the promise methods have to be attached" 
// see https://api.jquery.com/deferred.promise/ 

Definendo un costruttore con un mazzo di metodi, qualsiasi jQuery differite o promessa può essere esteso con la sintassi semplice

.promise(Constructor()) 

nel mio inedito, documentato jQuery promette giochi per bambini, il costruttore è chiamato $P e mantenuto nello spazio dei nomi jQuery, quindi la sintassi attuale che uso è:

.promise($.$P()) 

È necessario essere consapevoli di questo, per la maggior parte, non è necessario chiamare $.$P() esplicitamente come il parco giochi include un metodo $.when_() che restituisce una promessa già esteso.

Ecco una versione abbreviata del Parco giochi con appena sufficiente a fornire un metodo .delay():

(function($) { 
    /* *********************************** 
    * The $.$P function returns an object 
    * designed to be extended with 
    * promise methods using the syntax : 
    * myDeferred.promise($.$P()) 
    * myPromise.promise($.$P()) 
    * where `myDeferred`/`myPromise` 
    * are jQuery Deferred/Promise objects. 
    * ***********************************/ 

    /* *********************************** 
    * Methods 
    * ***********************************/ 
    $.$P = function() { 
     if (this instanceof $.$P) { 
      return this; 
     } else { 
      return new $.$P(); 
     } 
    }; 
    $.$P.prototype.then_ = function(fa, fb) { 
     /* A promise method that is the same as .then() 
     * but makes these extra methods available 
     * down-chain. 
     */ 
     return this.then(fa||null, fb||null).promise($.$P()); 
    } 
    $.$P.prototype.delay_ = function(ms) { 
     /* A promise method that 
     * introduces a down-chain delay. 
     */ 
     var promise = this; 
     function f(method) { 
      return function() { setTimeout(function(){ method.apply(null,this); }.bind(arguments), ms||0); }; 
     } 
     return $.Deferred(function(dfrd) { 
      promise.then(f(dfrd.resolve), f(dfrd.reject)); 
     }).promise($.$P()); 
    } 

    /* *********************************** 
    * Utility functions 
    * ***********************************/ 
    function consolidate(args) { 
     /* Convert mixed promises/arrays_of_promises to single array. 
     * Called by all the when_() methods below. 
     */ 
     return Array.prototype.slice.apply(args).reduce(function(arr, current) { 
      return arr.concat(current); 
     }, []); 
    } 

    /* *********************************** 
    * This section extends the jQuery namespace 
    * with a "jQuery.when_()" method. 
    * *********************************** 
    */ 
    $.extend({ 
     'when_': function() { 
      return $.when.apply(null, consolidate(arguments)).promise($.$P()).then_(function() { 
       return consolidate(arguments); 
      }); 
     }, 
    }); 
})(jQuery); 

La piena Playground include anche un sacco più statico e metodi promessa istanza per altri scopi, e in via di sviluppo è l'essenza del gioco.

Le regole di base per l'utilizzo del Playgound sono i seguenti:

  • Tutti i metodi statici e la promessa del parco giochi finiscono in "_" underscore.
  • I metodi statici, ad esempio $.when_(), sono resi disponibili solo installando Playgound.
  • Le promesse in una catena di promesse vengono estese includendo un metodo statico, ad esempio .when_() o concatenando .promise($.$P()).
  • In una catena di promesse, le estensioni rimangono disponibili (lungo la catena) utilizzando i metodi "..._" anziché i metodi standard, ad esempio .then_() in sostituzione di .then().

Quindi, ecco come usarlo per imporre i ritardi richiesti dalla domanda:

jQuery(function($) { 
    var MYNAMESPACE = { 
     run: function (t) { 
      return $.when_() 
      .then_(function() { 
       log("call together"); 
       log("call together");  
      }) 
      .delay_(t) 
      .then_(function() { 
       log("call first"); 
      }) 
      .delay_(t) 
      .then_(function() { 
       log("call second"); 
      }); 
     } 
    } 
}); 

DEMO

Nella demo, gestore click del pulsante dà ulteriore indicazione di come il parco giochi può essere utilizzata.

riserve sull'utilizzo del parco giochi:

  • Come dico - è un parco giochi .
  • Come un adattatore per jQuery, non una patch, è orribilmente inefficiente in alcuni punti. L'aspetto peggiore è che alcuni metodi creano una promessa intermedia oltre a quella che restituiscono.
  • Non testato secondo gli standard richiesti per l'uso nel codice di produzione, quindi usare con cautela.

Infine, considerare solo quanto sopra se si è determinati ad implementare il ritardo con jQuery. È molto più semplice usare una prom promise che abbia già un metodo .delay().

+0

Sei padrone delle promesse. Grazie. – user348173

0

Modifica, aggiornato

Prova ad aggiungere proprietà delay-0.123.

delay: function(t) { 
     return this.then(function() { 
     var args = arguments; 
     return new $.Deferred(function(d) { 
      setTimeout(function() { 
      // return `data`, if any, to next method, e.g., `.then`, in chain 
      d.resolveWith(this, args) 
      }.bind(this), t || 0) 
     }).promise() 
     }) 
    } 

(function($) { 
 
    $.Deferred = function(a) { 
 
    var b = [ 
 
     ["resolve", "done", $.Callbacks("once memory"), "resolved"], 
 
     ["reject", "fail", $.Callbacks("once memory"), "rejected"], 
 
     ["notify", "progress", $.Callbacks("memory")] 
 
     ], 
 
     c = "pending", 
 
     d = { 
 
     delay: function(t) { 
 
      return this.then(function() { 
 
      var args = arguments; 
 
      return new $.Deferred(function(d) { 
 
       setTimeout(function() { 
 
       // return `data`, if any, to next method, e.g., `.then`, in chain 
 
       d.resolveWith(this, args) 
 
       }.bind(this), t || 0) 
 
      }).promise() 
 
      }) 
 
     }, 
 
     state: function() { 
 
      return c 
 
     }, 
 
     always: function() { 
 
      return e.done(arguments).fail(arguments), this 
 
     }, 
 
     then: function() { 
 
      var a = arguments; 
 
      return $.Deferred(function(c) { 
 
      $.each(b, function(b, f) { 
 
       var g = $.isFunction(a[b]) && a[b]; 
 
       e[f[1]](function() { 
 
       var a = g && g.apply(this, arguments); 
 
       a && $.isFunction(a.promise) 
 
       ? a.promise() 
 
        .done(c.resolve) 
 
        .fail(c.reject) 
 
        .progress(c.notify) 
 
       : c[f[0] + "With"](this === d 
 
        ? c.promise() 
 
        : this, g ? [a] : arguments) 
 
       }) 
 
      }), a = null 
 
      }).promise() 
 
     }, 
 
     promise: function(a) { 
 
      return null != a ? $.extend(a, d) : d 
 
     } 
 
     }, 
 
     e = {}; 
 
    return d.pipe = d.then, $.each(b, function(a, f) { 
 
     var g = f[2], 
 
     h = f[3]; 
 
     d[f[1]] = g.add, h && g.add(function() { 
 
     c = h 
 
     }, b[1^a][2].disable, b[2][2].lock), e[f[0]] = function() { 
 
     return e[f[0] + "With"](this === e ? d : this, arguments), this 
 
     }, e[f[0] + "With"] = g.fireWith 
 
    }), d.promise(e), a && a.call(e, e), e 
 
    } 
 

 
}(jQuery)); 
 

 
var p = { 
 
    run: function() { 
 
    return $() 
 
     .promise() 
 
     .then(function() { 
 
     console.log("call together"); 
 
     console.log("call together"); 
 
     // do stuff 
 
     // pass `data` to next `.then` 
 
     return "call first"; 
 
     }) 
 
     .delay(2000) 
 
     .then(function(data) { 
 
     console.log(data); 
 
     }) 
 
     .delay(2000) 
 
     .then(function() { 
 
     console.log("call second"); 
 
     }) 
 
    } 
 
}; 
 

 
p.run();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"> 
 
</script>

+0

"downvote" descrizione? – guest271314

+0

OP ha dato requisiti molto specifici di ciò che vuole scrivere. La tua risposta non è conforme a questo. Imo è anche peggio della sua soluzione di lavoro con '.then (delay (2000))' – Bergi

+0

Lo direi, sì, anche se non prenderei in considerazione la sostituzione completa di '$ .Deferred' con un'altra implementazione per essere una buona soluzione . E se intendete usare 'resolveWith', probabilmente dovreste passare l'originale' this' e 'arguments' dal callback' then', non solo il primo parametro. – Bergi

0

Ecco la mia soluzione. Confezionamento $.Deferred(afterBuild) e impacchetta il afterBuild originale che a sua volta avvolge il metodo .promise(obj), estendendo il dato obj con il metodo personalizzato delay. Che utilizza window.setTimeout:

Nota: si ritarda solo il ramo done.

function extendPromises(extensions) { 
 
    $.Deferred = (function (originalDeferred) { 
 
     return function (afterBuild) { 
 
      var newAfterBuild = function (d) { 
 
       d.promise = (function (originalPromise) { 
 
        return function (obj) { 
 
         return originalPromise.call(this, $.extend(obj, extensions)); 
 
        }; 
 
       })(d.promise); 
 
       if (afterBuild) afterBuild.apply(this, arguments); 
 
       return this; 
 
      }; 
 
      return originalDeferred.call(this, newAfterBuild); 
 
     }; 
 
    })($.Deferred); 
 
} 
 

 
extendPromises({ 
 
    delay: function (delay) { 
 
     return this.then(function (value) { 
 
      var d = $.Deferred(); 
 
      window.setTimeout(function() { 
 
       d.resolve(value); 
 
      }, delay); 
 
      return d.promise(); 
 
     }); 
 
    } 
 
}); 
 

 
// so now I can do: 
 
$.when("hello") 
 
.then(function (value) { $("#log").append(value+"\n"); return value; }) 
 
.delay(1000) 
 
.then(function (value) { $("#log").append(value); return value; });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<textarea id="log" rows=5></textarea>