mirror of
https://github.com/jquery/jquery.git
synced 2024-12-25 13:14:21 +00:00
Core: implement ready without Deferred
- Make jQuery.ready promise-compatible - Gives up sync guarantee for post-ready callbacks Fixes gh-1778 Fixes gh-1823 Close gh-2891
This commit is contained in:
parent
6072d150d6
commit
5cbb234dd3
@ -63,6 +63,10 @@ module.exports = function( grunt ) {
|
|||||||
callbacks: [ "deferred" ],
|
callbacks: [ "deferred" ],
|
||||||
css: [ "effects", "dimensions", "offset" ],
|
css: [ "effects", "dimensions", "offset" ],
|
||||||
"css/showHide": [ "effects" ],
|
"css/showHide": [ "effects" ],
|
||||||
|
deferred: {
|
||||||
|
remove: [ "ajax", "effects", "queue", "core/ready" ],
|
||||||
|
include: [ "core/ready-no-deferred" ]
|
||||||
|
},
|
||||||
sizzle: [ "css/hiddenVisibleSelectors", "effects/animatedSelector" ]
|
sizzle: [ "css/hiddenVisibleSelectors", "effects/animatedSelector" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,8 @@ module.exports = function( grunt ) {
|
|||||||
* whether it should included or excluded
|
* whether it should included or excluded
|
||||||
*/
|
*/
|
||||||
excluder = function( flag ) {
|
excluder = function( flag ) {
|
||||||
var m = /^(\+|\-|)([\w\/-]+)$/.exec( flag ),
|
var additional,
|
||||||
|
m = /^(\+|\-|)([\w\/-]+)$/.exec( flag ),
|
||||||
exclude = m[ 1 ] === "-",
|
exclude = m[ 1 ] === "-",
|
||||||
module = m[ 2 ];
|
module = m[ 2 ];
|
||||||
|
|
||||||
@ -192,8 +193,16 @@ module.exports = function( grunt ) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
additional = removeWith[ module ];
|
||||||
|
|
||||||
// Check removeWith list
|
// Check removeWith list
|
||||||
excludeList( removeWith[ module ] );
|
if ( additional ) {
|
||||||
|
excludeList( additional.remove || additional );
|
||||||
|
if ( additional.include ) {
|
||||||
|
included = included.concat( additional.include );
|
||||||
|
grunt.log.writeln( "+" + additional.include );
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
grunt.log.error( "Module \"" + module + "\" is a minimum requirement." );
|
grunt.log.error( "Module \"" + module + "\" is a minimum requirement." );
|
||||||
if ( module === "selector" ) {
|
if ( module === "selector" ) {
|
||||||
|
109
src/core/ready-no-deferred.js
Normal file
109
src/core/ready-no-deferred.js
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
define( [
|
||||||
|
"../core",
|
||||||
|
"../var/document"
|
||||||
|
], function( jQuery, document ) {
|
||||||
|
|
||||||
|
var readyCallbacks = [],
|
||||||
|
readyFiring = false,
|
||||||
|
whenReady = function( fn ) {
|
||||||
|
readyCallbacks.push( fn );
|
||||||
|
},
|
||||||
|
executeReady = function( fn ) {
|
||||||
|
|
||||||
|
// Prevent errors from freezing future callback execution (gh-1823)
|
||||||
|
// Not backwards-compatible as this does not execute sync
|
||||||
|
window.setTimeout( function() {
|
||||||
|
fn.call( document, jQuery );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
jQuery.fn.ready = function( fn ) {
|
||||||
|
whenReady( fn );
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
jQuery.extend( {
|
||||||
|
|
||||||
|
// Is the DOM ready to be used? Set to true once it occurs.
|
||||||
|
isReady: false,
|
||||||
|
|
||||||
|
// A counter to track how many items to wait for before
|
||||||
|
// the ready event fires. See #6781
|
||||||
|
readyWait: 1,
|
||||||
|
|
||||||
|
// Hold (or release) the ready event
|
||||||
|
holdReady: function( hold ) {
|
||||||
|
if ( hold ) {
|
||||||
|
jQuery.readyWait++;
|
||||||
|
} else {
|
||||||
|
jQuery.ready( true );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ready: function( wait ) {
|
||||||
|
|
||||||
|
// Abort if there are pending holds or we're already ready
|
||||||
|
if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remember that the DOM is ready
|
||||||
|
jQuery.isReady = true;
|
||||||
|
|
||||||
|
// If a normal DOM Ready event fired, decrement, and wait if need be
|
||||||
|
if ( wait !== true && --jQuery.readyWait > 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
whenReady = function( fn ) {
|
||||||
|
readyCallbacks.push( fn );
|
||||||
|
|
||||||
|
if ( !readyFiring ) {
|
||||||
|
readyFiring = true;
|
||||||
|
|
||||||
|
while ( readyCallbacks.length ) {
|
||||||
|
fn = readyCallbacks.shift();
|
||||||
|
if ( jQuery.isFunction( fn ) ) {
|
||||||
|
executeReady( fn );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
readyFiring = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
whenReady();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Make jQuery.ready Promise consumable (gh-1778)
|
||||||
|
jQuery.ready.then = jQuery.fn.ready;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ready event handler and self cleanup method
|
||||||
|
*/
|
||||||
|
function completed() {
|
||||||
|
document.removeEventListener( "DOMContentLoaded", completed );
|
||||||
|
window.removeEventListener( "load", completed );
|
||||||
|
jQuery.ready();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Catch cases where $(document).ready() is called
|
||||||
|
// after the browser event has already occurred.
|
||||||
|
// Support: IE9-10 only
|
||||||
|
// Older IE sometimes signals "interactive" too soon
|
||||||
|
if ( document.readyState === "complete" ||
|
||||||
|
( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
|
||||||
|
|
||||||
|
// Handle it asynchronously to allow scripts the opportunity to delay ready
|
||||||
|
window.setTimeout( jQuery.ready );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Use the handy event callback
|
||||||
|
document.addEventListener( "DOMContentLoaded", completed );
|
||||||
|
|
||||||
|
// A fallback to window.onload, that will always work
|
||||||
|
window.addEventListener( "load", completed );
|
||||||
|
}
|
||||||
|
|
||||||
|
} );
|
@ -5,12 +5,11 @@ define( [
|
|||||||
], function( jQuery, document ) {
|
], function( jQuery, document ) {
|
||||||
|
|
||||||
// The deferred used on DOM ready
|
// The deferred used on DOM ready
|
||||||
var readyList;
|
var readyList = jQuery.Deferred();
|
||||||
|
|
||||||
jQuery.fn.ready = function( fn ) {
|
jQuery.fn.ready = function( fn ) {
|
||||||
|
|
||||||
// Add the callback
|
readyList.then( fn );
|
||||||
jQuery.ready.promise().done( fn );
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
@ -54,43 +53,32 @@ jQuery.extend( {
|
|||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
/**
|
jQuery.ready.then = readyList.then;
|
||||||
* The ready event handler and self cleanup method
|
|
||||||
*/
|
// The ready event handler and self cleanup method
|
||||||
function completed() {
|
function completed() {
|
||||||
document.removeEventListener( "DOMContentLoaded", completed );
|
document.removeEventListener( "DOMContentLoaded", completed );
|
||||||
window.removeEventListener( "load", completed );
|
window.removeEventListener( "load", completed );
|
||||||
jQuery.ready();
|
jQuery.ready();
|
||||||
}
|
}
|
||||||
|
|
||||||
jQuery.ready.promise = function( obj ) {
|
// Catch cases where $(document).ready() is called
|
||||||
if ( !readyList ) {
|
// after the browser event has already occurred.
|
||||||
|
// Support: IE <=9 - 10 only
|
||||||
|
// Older IE sometimes signals "interactive" too soon
|
||||||
|
if ( document.readyState === "complete" ||
|
||||||
|
( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
|
||||||
|
|
||||||
readyList = jQuery.Deferred();
|
// Handle it asynchronously to allow scripts the opportunity to delay ready
|
||||||
|
window.setTimeout( jQuery.ready );
|
||||||
|
|
||||||
// Catch cases where $(document).ready() is called
|
} else {
|
||||||
// after the browser event has already occurred.
|
|
||||||
// Support: IE <=9 - 10 only
|
|
||||||
// Older IE sometimes signals "interactive" too soon
|
|
||||||
if ( document.readyState === "complete" ||
|
|
||||||
( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
|
|
||||||
|
|
||||||
// Handle it asynchronously to allow scripts the opportunity to delay ready
|
// Use the handy event callback
|
||||||
window.setTimeout( jQuery.ready );
|
document.addEventListener( "DOMContentLoaded", completed );
|
||||||
|
|
||||||
} else {
|
// A fallback to window.onload, that will always work
|
||||||
|
window.addEventListener( "load", completed );
|
||||||
// Use the handy event callback
|
}
|
||||||
document.addEventListener( "DOMContentLoaded", completed );
|
|
||||||
|
|
||||||
// A fallback to window.onload, that will always work
|
|
||||||
window.addEventListener( "load", completed );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return readyList.promise( obj );
|
|
||||||
};
|
|
||||||
|
|
||||||
// Kick off the DOM ready check even if the user does not
|
|
||||||
jQuery.ready.promise();
|
|
||||||
|
|
||||||
} );
|
} );
|
||||||
|
@ -2,6 +2,7 @@ QUnit.module( "ready" );
|
|||||||
|
|
||||||
( function() {
|
( function() {
|
||||||
var notYetReady, noEarlyExecution,
|
var notYetReady, noEarlyExecution,
|
||||||
|
promisified = Promise.resolve( jQuery.ready ),
|
||||||
order = [],
|
order = [],
|
||||||
args = {};
|
args = {};
|
||||||
|
|
||||||
@ -26,13 +27,36 @@ QUnit.module( "ready" );
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function throwError( num ) {
|
||||||
|
|
||||||
|
// Not a global QUnit failure
|
||||||
|
var onerror = window.onerror;
|
||||||
|
window.onerror = function() {
|
||||||
|
window.onerror = onerror;
|
||||||
|
};
|
||||||
|
|
||||||
|
throw new Error( "Ready error " + num );
|
||||||
|
}
|
||||||
|
|
||||||
// Bind to the ready event in every possible way.
|
// Bind to the ready event in every possible way.
|
||||||
jQuery( makeHandler( "a" ) );
|
jQuery( makeHandler( "a" ) );
|
||||||
jQuery( document ).ready( makeHandler( "b" ) );
|
jQuery( document ).ready( makeHandler( "b" ) );
|
||||||
|
|
||||||
|
// Throw in an error to ensure other callbacks are called
|
||||||
|
jQuery( function() {
|
||||||
|
throwError( 1 );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Throw two errors in a row
|
||||||
|
jQuery( function() {
|
||||||
|
throwError( 2 );
|
||||||
|
} );
|
||||||
|
jQuery.when( jQuery.ready ).done( makeHandler( "c" ) );
|
||||||
|
|
||||||
// Do it twice, just to be sure.
|
// Do it twice, just to be sure.
|
||||||
jQuery( makeHandler( "c" ) );
|
jQuery( makeHandler( "d" ) );
|
||||||
jQuery( document ).ready( makeHandler( "d" ) );
|
jQuery( document ).ready( makeHandler( "e" ) );
|
||||||
|
jQuery.when( jQuery.ready ).done( makeHandler( "f" ) );
|
||||||
|
|
||||||
noEarlyExecution = order.length === 0;
|
noEarlyExecution = order.length === 0;
|
||||||
|
|
||||||
@ -44,7 +68,7 @@ QUnit.module( "ready" );
|
|||||||
"Handlers bound to DOM ready should not execute before DOM ready" );
|
"Handlers bound to DOM ready should not execute before DOM ready" );
|
||||||
|
|
||||||
// Ensure execution order.
|
// Ensure execution order.
|
||||||
assert.deepEqual( order, [ "a", "b", "c", "d" ],
|
assert.deepEqual( order, [ "a", "b", "c", "d", "e", "f" ],
|
||||||
"Bound DOM ready handlers should execute in on-order" );
|
"Bound DOM ready handlers should execute in on-order" );
|
||||||
|
|
||||||
// Ensure handler argument is correct.
|
// Ensure handler argument is correct.
|
||||||
@ -55,16 +79,48 @@ QUnit.module( "ready" );
|
|||||||
|
|
||||||
order = [];
|
order = [];
|
||||||
|
|
||||||
// Now that the ready event has fired, again bind to the ready event
|
// Now that the ready event has fired, again bind to the ready event.
|
||||||
// in every possible way. These event handlers should execute immediately.
|
// These ready handlers should execute asynchronously.
|
||||||
|
var done = assert.async();
|
||||||
jQuery( makeHandler( "g" ) );
|
jQuery( makeHandler( "g" ) );
|
||||||
assert.equal( order.pop(), "g", "Event handler should execute immediately" );
|
|
||||||
assert.equal( args.g, jQuery, "Argument passed to fn in jQuery( fn ) should be jQuery" );
|
|
||||||
|
|
||||||
jQuery( document ).ready( makeHandler( "h" ) );
|
jQuery( document ).ready( makeHandler( "h" ) );
|
||||||
assert.equal( order.pop(), "h", "Event handler should execute immediately" );
|
window.setTimeout( function() {
|
||||||
assert.equal( args.h, jQuery,
|
assert.equal( order.shift(), "g", "Event handler should execute immediately, but async" );
|
||||||
"Argument passed to fn in jQuery(document).ready( fn ) should be jQuery" );
|
assert.equal( args.g, jQuery, "Argument passed to fn in jQuery( fn ) should be jQuery" );
|
||||||
|
|
||||||
|
assert.equal( order.shift(), "h", "Event handler should execute immediately, but async" );
|
||||||
|
assert.equal( args.h, jQuery,
|
||||||
|
"Argument passed to fn in jQuery(document).ready( fn ) should be jQuery" );
|
||||||
|
done();
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
QUnit.test( "Promise.resolve(jQuery.ready)", function( assert ) {
|
||||||
|
assert.expect( 2 );
|
||||||
|
var done = jQuery.map( new Array( 2 ), function() { return assert.async(); } );
|
||||||
|
|
||||||
|
promisified.then( function() {
|
||||||
|
assert.ok( jQuery.isReady, "Native promised resolved" );
|
||||||
|
done.pop()();
|
||||||
|
} );
|
||||||
|
|
||||||
|
Promise.resolve( jQuery.ready ).then( function() {
|
||||||
|
assert.ok( jQuery.isReady, "Native promised resolved" );
|
||||||
|
done.pop()();
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
QUnit.test( "Error in ready callback does not halt all future executions (gh-1823)", function( assert ) {
|
||||||
|
assert.expect( 1 );
|
||||||
|
var done = assert.async();
|
||||||
|
|
||||||
|
jQuery( function() {
|
||||||
|
throwError( 3 );
|
||||||
|
} );
|
||||||
|
|
||||||
|
jQuery( function() {
|
||||||
|
assert.ok( true, "Subsequent handler called" );
|
||||||
|
done();
|
||||||
|
} );
|
||||||
|
} );
|
||||||
} )();
|
} )();
|
||||||
|
Loading…
Reference in New Issue
Block a user