1+ /* globals MutationObserver */
2+
13// can we use __proto__?
24export const hasProto = '__proto__' in { }
35
@@ -14,6 +16,7 @@ const UA = inBrowser && window.navigator.userAgent.toLowerCase()
1416export const isIE = UA && UA . indexOf ( 'trident' ) > 0
1517export const isIE9 = UA && UA . indexOf ( 'msie 9.0' ) > 0
1618export const isAndroid = UA && UA . indexOf ( 'android' ) > 0
19+ export const isIOS = UA && / i p h o n e | i p a d | i p o d | i o s / . test ( UA )
1720
1821let transitionProp
1922let transitionEndEvent
@@ -49,6 +52,11 @@ export {
4952 animationEndEvent
5053}
5154
55+ /* istanbul ignore next */
56+ function isNative ( Ctor ) {
57+ return / n a t i v e c o d e / . test ( Ctor . toString ( ) )
58+ }
59+
5260/**
5361 * Defer a task to execute it asynchronously. Ideally this
5462 * should be executed as a microtask, so we leverage
@@ -60,34 +68,55 @@ export {
6068 */
6169
6270export const nextTick = ( function ( ) {
63- var callbacks = [ ]
64- var pending = false
65- var timerFunc
71+ const callbacks = [ ]
72+ let pending = false
73+ let timerFunc
74+
6675 function nextTickHandler ( ) {
6776 pending = false
68- var copies = callbacks . slice ( 0 )
69- callbacks = [ ]
70- for ( var i = 0 ; i < copies . length ; i ++ ) {
77+ const copies = callbacks . slice ( 0 )
78+ callbacks . length = 0
79+ for ( let i = 0 ; i < copies . length ; i ++ ) {
7180 copies [ i ] ( )
7281 }
7382 }
7483
75- /* istanbul ignore else */
76- if ( inBrowser && window . postMessage &&
77- ! window . importScripts && // not in WebWorker
78- ! ( isAndroid && ! window . requestAnimationFrame ) // not in Android <= 4.3
79- ) {
80- const NEXT_TICK_TOKEN = '__vue__nextTick__'
81- window . addEventListener ( 'message' , e => {
82- if ( e . source === window && e . data === NEXT_TICK_TOKEN ) {
83- nextTickHandler ( )
84- }
84+ // the nextTick behavior leverages the microtask queue, which can be accessed
85+ // via either native Promise.then or MutationObserver.
86+ // MutationObserver has wider support, however it is seriously bugged in
87+ // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
88+ // completely stops working after triggering a few times... so, if native
89+ // Promise is available, we will use it:
90+ /* istanbul ignore if */
91+ if ( typeof Promise !== 'undefined' && isNative ( Promise ) ) {
92+ var p = Promise . resolve ( )
93+ var noop = function ( ) { }
94+ timerFunc = ( ) => {
95+ p . then ( nextTickHandler )
96+ // in problematic UIWebViews, Promise.then doesn't completely break, but
97+ // it can get stuck in a weird state where callbacks are pushed into the
98+ // microtask queue but the queue isn't being flushed, until the browser
99+ // needs to do some other work, e.g. handle a timer. Therefore we can
100+ // "force" the microtask queue to be flushed by adding an empty timer.
101+ if ( isIOS ) setTimeout ( noop )
102+ }
103+ } else if ( typeof MutationObserver !== 'undefined' ) {
104+ // use MutationObserver where native Promise is not available,
105+ // e.g. IE11, iOS7, Android 4.4
106+ var counter = 1
107+ var observer = new MutationObserver ( nextTickHandler )
108+ var textNode = document . createTextNode ( String ( counter ) )
109+ observer . observe ( textNode , {
110+ characterData : true
85111 } )
86112 timerFunc = ( ) => {
87- window . postMessage ( NEXT_TICK_TOKEN , '*' )
113+ counter = ( counter + 1 ) % 2
114+ textNode . data = String ( counter )
88115 }
89116 } else {
90- timerFunc = ( typeof global !== 'undefined' && global . setImmediate ) || setTimeout
117+ // fallback to setTimeout
118+ /* istanbul ignore next */
119+ timerFunc = setTimeout
91120 }
92121
93122 return function ( cb , ctx ) {
@@ -103,7 +132,7 @@ export const nextTick = (function () {
103132
104133let _Set
105134/* istanbul ignore if */
106- if ( typeof Set !== 'undefined' && Set . toString ( ) . match ( / n a t i v e c o d e / ) ) {
135+ if ( typeof Set !== 'undefined' && isNative ( Set ) ) {
107136 // use native Set when available.
108137 _Set = Set
109138} else {
0 commit comments