mirror of
https://github.com/jquery/jquery.git
synced 2024-12-09 08:04:24 +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" ],
|
||||
css: [ "effects", "dimensions", "offset" ],
|
||||
"css/showHide": [ "effects" ],
|
||||
deferred: {
|
||||
remove: [ "ajax", "effects", "queue", "core/ready" ],
|
||||
include: [ "core/ready-no-deferred" ]
|
||||
},
|
||||
sizzle: [ "css/hiddenVisibleSelectors", "effects/animatedSelector" ]
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +168,8 @@ module.exports = function( grunt ) {
|
||||
* whether it should included or excluded
|
||||
*/
|
||||
excluder = function( flag ) {
|
||||
var m = /^(\+|\-|)([\w\/-]+)$/.exec( flag ),
|
||||
var additional,
|
||||
m = /^(\+|\-|)([\w\/-]+)$/.exec( flag ),
|
||||
exclude = m[ 1 ] === "-",
|
||||
module = m[ 2 ];
|
||||
|
||||
@ -192,8 +193,16 @@ module.exports = function( grunt ) {
|
||||
}
|
||||
}
|
||||
|
||||
additional = removeWith[ module ];
|
||||
|
||||
// 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 {
|
||||
grunt.log.error( "Module \"" + module + "\" is a minimum requirement." );
|
||||
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 ) {
|
||||
|
||||
// The deferred used on DOM ready
|
||||
var readyList;
|
||||
var readyList = jQuery.Deferred();
|
||||
|
||||
jQuery.fn.ready = function( fn ) {
|
||||
|
||||
// Add the callback
|
||||
jQuery.ready.promise().done( fn );
|
||||
readyList.then( fn );
|
||||
|
||||
return this;
|
||||
};
|
||||
@ -54,43 +53,32 @@ jQuery.extend( {
|
||||
}
|
||||
} );
|
||||
|
||||
/**
|
||||
* The ready event handler and self cleanup method
|
||||
*/
|
||||
jQuery.ready.then = readyList.then;
|
||||
|
||||
// The ready event handler and self cleanup method
|
||||
function completed() {
|
||||
document.removeEventListener( "DOMContentLoaded", completed );
|
||||
window.removeEventListener( "load", completed );
|
||||
jQuery.ready();
|
||||
}
|
||||
|
||||
jQuery.ready.promise = function( obj ) {
|
||||
if ( !readyList ) {
|
||||
// Catch cases where $(document).ready() is called
|
||||
// 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
|
||||
// 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 ) ) {
|
||||
} else {
|
||||
|
||||
// Handle it asynchronously to allow scripts the opportunity to delay ready
|
||||
window.setTimeout( jQuery.ready );
|
||||
// Use the handy event callback
|
||||
document.addEventListener( "DOMContentLoaded", completed );
|
||||
|
||||
} else {
|
||||
|
||||
// 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();
|
||||
// A fallback to window.onload, that will always work
|
||||
window.addEventListener( "load", completed );
|
||||
}
|
||||
|
||||
} );
|
||||
|
@ -2,6 +2,7 @@ QUnit.module( "ready" );
|
||||
|
||||
( function() {
|
||||
var notYetReady, noEarlyExecution,
|
||||
promisified = Promise.resolve( jQuery.ready ),
|
||||
order = [],
|
||||
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.
|
||||
jQuery( makeHandler( "a" ) );
|
||||
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.
|
||||
jQuery( makeHandler( "c" ) );
|
||||
jQuery( document ).ready( makeHandler( "d" ) );
|
||||
jQuery( makeHandler( "d" ) );
|
||||
jQuery( document ).ready( makeHandler( "e" ) );
|
||||
jQuery.when( jQuery.ready ).done( makeHandler( "f" ) );
|
||||
|
||||
noEarlyExecution = order.length === 0;
|
||||
|
||||
@ -44,7 +68,7 @@ QUnit.module( "ready" );
|
||||
"Handlers bound to DOM ready should not execute before DOM ready" );
|
||||
|
||||
// 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" );
|
||||
|
||||
// Ensure handler argument is correct.
|
||||
@ -55,16 +79,48 @@ QUnit.module( "ready" );
|
||||
|
||||
order = [];
|
||||
|
||||
// Now that the ready event has fired, again bind to the ready event
|
||||
// in every possible way. These event handlers should execute immediately.
|
||||
// Now that the ready event has fired, again bind to the ready event.
|
||||
// These ready handlers should execute asynchronously.
|
||||
var done = assert.async();
|
||||
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" ) );
|
||||
assert.equal( order.pop(), "h", "Event handler should execute immediately" );
|
||||
assert.equal( args.h, jQuery,
|
||||
"Argument passed to fn in jQuery(document).ready( fn ) should be jQuery" );
|
||||
window.setTimeout( function() {
|
||||
assert.equal( order.shift(), "g", "Event handler should execute immediately, but async" );
|
||||
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