[wp-trac] [WordPress Trac] #38889: Proposition to allow an api.Value() using asynchronous callbacks
WordPress Trac
noreply at wordpress.org
Mon Nov 21 17:22:11 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 | Resolution:
Keywords: reporter-feedback | Focuses: javascript
-------------------------------+------------------------------
Changes (by westonruter):
* keywords: => reporter-feedback
Old description:
> 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 !
New description:
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 :
{{{#!js
//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.
{{{#!js
/**
* 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 !
--
Comment:
@nikeo I'm not sure I understand. In your example here:
{{{#!js
api.observable_value.bind( fakeAjaxCall, {deferred : true } );
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() );
});
}}}
In this case, the value of “new_val” is basically temporary throw-away,
isn't it? You're ultimately wanting to get the value from an Ajax request?
It seems to me that you should rather just call `fakeAjaxCall` directly
and let it set the value once. What is the use case you're looking at?
--
Ticket URL: <https://core.trac.wordpress.org/ticket/38889#comment:2>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list