[wp-trac] [WordPress Trac] #38889: Proposition to allow an api.Value() using asynchronous callbacks

WordPress Trac noreply at wordpress.org
Mon Nov 21 16:50:20 UTC 2016


#38889: Proposition to allow an api.Value() using asynchronous callbacks
-------------------------+-----------------------------
 Reporter:  nikeo        |      Owner:
     Type:  enhancement  |     Status:  new
 Priority:  normal       |  Milestone:  Awaiting Review
Component:  Customize    |    Version:  trunk
 Severity:  normal       |   Keywords:
  Focuses:  javascript   |
-------------------------+-----------------------------
 When an `api.Value` instance is set to a new value, we might need to wait
 for all callbacks to be completed or resolved before actually doing
 something else.
 With the current implementation, if an `api.Value` has asynchronous
 callbacks, there's no way to know when it's resolved after a `set()`
 action. Any further dependant actions would have to be done after an
 hazardous delay.

 The following is a proposition to solve this by adding an optional `{
 deferred : true }` param when binding a callback to an `api.Value`
 instance.

 This way, a syntax like this could be used :

 {{{

 //let's declare a new api Value().
 api.observable_value = new api Value( 'initial' );

 //this is basic callback with a $.Deferred callback
 var fakeAjaxCall = function( to, from ) {
     var dfd = $.Deferred();

     _.delay( function() {
         console.log( 'I am a fake ajax call.');
         dfd.resolve( to );
     }, 2000 );

     return dfd.promise();
 };

 //let's bind the Value instance with this callback and inform it that it
 is a deferred action
 api.observable_value.bind( fakeAjaxCall, {deferred : true } );

 //now let's set update the value
 api.observable_value.set( 'new_val' ).done( function() {
     //do something with this new_val when all callbacks are done
     console.log('All callbacks are done now, we can use the new val in
 further dependant actions : ', this() );
 });
 }}}


 This could be useful for callbacks involving preview refreshs ( if the
 preview returns a `promise()` , see #38797), or media fetched in ajax in
 db.


 '''Code'''
 This is a proposition of modification of the `api.Value` constructor, in
 the current customize-base.js file. ( as of 4.7 beta-3 )

 The code introduces an optional additional collection of `$.Deferred`
 functions, as a property of the `api.Value` constructor.


 {{{
 /**
  * Set the value and trigger all bound callbacks.
  * @return a promise onto which it is attached the current 'this' of the
 api.Value, when all callbacks are resolved.
  * @param {object} to New value.
  */
 set: function( to ) {
     var from = this._value, dfd = $.Deferred(), self = this, _promises =
 [];

     to = this._setter.apply( this, arguments );
     to = this.validate( to );

     // Bail if the sanitized value is null or unchanged.
     if ( null === to || _.isEqual( from, to ) ) {
       return this;
     }

     this._value = to;
     this._dirty = true;

     if ( this._deferreds ) {
           _.each( self._deferreds, function( _prom ) {
                 _promises.push( _prom.apply( null, [ to, from ] ) );
           });

           $.when.apply( null, _promises )
                 .then( function() {
                       self.callbacks.fireWith( self, [ to, from ] );
                       dfd.resolveWith( self, [ to, from ] );
                 });
     } else {
           this.callbacks.fireWith( this, [ to, from ] );
           return dfd.resolveWith( self, [ to, from ] ).promise( self );
     }
     return dfd.promise( self );
 }


 /**
  * Bind a function to be invoked whenever the value changes.
  *
  * @param {...Function} A function, or multiple functions, to add to the
 callback stack.
  * @param { deferred : true } let us decide if the callback(s) have to be
 populated as $.Callbacks or $.Deferred
  */
 bind: function() {
   var self = this,
       _isDeferred = false,
       _cbs = [];

   $.each( arguments, function( _key, _arg ) {
         if ( ! _isDeferred )
           _isDeferred = _.isObject( _arg  ) && _arg.deferred;
         if ( _.isFunction( _arg ) )
           _cbs.push( _arg );
   });

   if ( _isDeferred ) {
         self._deferreds = self._deferreds || [];
         _.each( _cbs, function( _cb ) {
               if ( ! _.contains( _cb, self._deferreds ) )
                 self._deferreds.push( _cb );
         });
   } else {
         //original method
         self.callbacks.add.apply( self.callbacks, arguments );
   }
   return this;
 }
 }}}

 The `unbind` method of the `api.Value` constructor is missing here, it
 should also be modified.
 Any thoughts or feedbacks are welcome !

--
Ticket URL: <https://core.trac.wordpress.org/ticket/38889>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list