11/* @flow */
2+ /* globals MutationObserver */
3+
4+ import { noop } from 'shared/util'
25
36// can we use __proto__?
47export const hasProto = '__proto__' in { }
@@ -13,61 +16,84 @@ export const isIE = UA && /msie|trident/.test(UA)
1316export const isIE9 = UA && UA . indexOf ( 'msie 9.0' ) > 0
1417export const isEdge = UA && UA . indexOf ( 'edge/' ) > 0
1518export 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 )
1620
1721// detect devtools
1822export const devtools = inBrowser && window . __VUE_DEVTOOLS_GLOBAL_HOOK__
1923
24+ function isNative ( Ctor : Function ) : boolean {
25+ return / n a t i v e c o d e / . test ( Ctor . toString ( ) )
26+ }
27+
2028/**
2129 * Defer a task to execute it asynchronously. Ideally this
2230 * should be executed as a microtask, but MutationObserver is unreliable
2331 * in iOS UIWebView so we use a setImmediate shim and fallback to setTimeout.
2432 */
2533export const nextTick = ( function ( ) {
26- let callbacks = [ ]
34+ const callbacks = [ ]
2735 let pending = false
2836 let timerFunc
2937
3038 function nextTickHandler ( ) {
3139 pending = false
3240 const copies = callbacks . slice ( 0 )
33- callbacks = [ ]
41+ callbacks . length = 0
3442 for ( let i = 0 ; i < copies . length ; i ++ ) {
3543 copies [ i ] ( )
3644 }
3745 }
3846
39- /* istanbul ignore else */
40- if ( inBrowser && window . postMessage &&
41- ! window . importScripts && // not in WebWorker
42- ! ( isAndroid && ! window . requestAnimationFrame ) // not in Android <= 4.3
43- ) {
44- const NEXT_TICK_TOKEN = '__vue__nextTick__'
45- window . addEventListener ( 'message' , e => {
46- if ( e . source === window && e . data === NEXT_TICK_TOKEN ) {
47- nextTickHandler ( )
48- }
47+ // the nextTick behavior leverages the microtask queue, which can be accessed
48+ // via either native Promise.then or MutationObserver.
49+ // MutationObserver has wider support, however it is seriously bugged in
50+ // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
51+ // completely stops working after triggering a few times... so, if native
52+ // Promise is available, we will use it:
53+ if ( typeof Promise !== 'undefined' && isNative ( Promise ) ) {
54+ var p = Promise . resolve ( )
55+ timerFunc = ( ) => {
56+ p . then ( nextTickHandler )
57+ // in problematic UIWebViews, Promise.then doesn't completely break, but
58+ // it can get stuck in a weird state where callbacks are pushed into the
59+ // microtask queue but the queue isn't being flushed, until the browser
60+ // needs to do some other work, e.g. handle a timer. Therefore we can
61+ // "force" the microtask queue to be flushed by adding an empty timer.
62+ if ( isIOS ) setTimeout ( noop )
63+ }
64+ } else if ( typeof MutationObserver !== 'undefined' ) {
65+ // use MutationObserver where native Promise is not available,
66+ // e.g. IE11, iOS7, Android 4.4
67+ var counter = 1
68+ var observer = new MutationObserver ( nextTickHandler )
69+ var textNode = document . createTextNode ( String ( counter ) )
70+ observer . observe ( textNode , {
71+ characterData : true
4972 } )
5073 timerFunc = ( ) => {
51- window . postMessage ( NEXT_TICK_TOKEN , '*' )
74+ counter = ( counter + 1 ) % 2
75+ textNode . data = String ( counter )
5276 }
5377 } else {
54- timerFunc = ( typeof global !== 'undefined' && global . setImmediate ) || setTimeout
78+ // fallback to setTimeout
79+ timerFunc = setTimeout
5580 }
5681
5782 return function queueNextTick ( cb : Function , ctx ?: Object ) {
5883 const func = ctx
5984 ? function ( ) { cb . call ( ctx ) }
6085 : cb
6186 callbacks . push ( func )
62- if ( pending ) return
63- pending = true
64- timerFunc ( nextTickHandler , 0 )
87+ if ( ! pending ) {
88+ pending = true
89+ timerFunc ( nextTickHandler , 0 )
90+ }
6591 }
6692} ) ( )
6793
6894let _Set
6995/* istanbul ignore if */
70- if ( typeof Set !== 'undefined' && / n a t i v e c o d e / . test ( Set . toString ( ) ) ) {
96+ if ( typeof Set !== 'undefined' && isNative ( Set ) ) {
7197 // use native Set when available.
7298 _Set = Set
7399} else {
0 commit comments