Skip to content

Commit 64f7113

Browse files
committed
revert nextTick to microtask semantics by using Promise.then
1 parent baa92ca commit 64f7113

File tree

1 file changed

+48
-19
lines changed

1 file changed

+48
-19
lines changed

src/util/env.js

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/* globals MutationObserver */
2+
13
// can we use __proto__?
24
export const hasProto = '__proto__' in {}
35

@@ -14,6 +16,7 @@ const UA = inBrowser && window.navigator.userAgent.toLowerCase()
1416
export const isIE = UA && UA.indexOf('trident') > 0
1517
export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
1618
export const isAndroid = UA && UA.indexOf('android') > 0
19+
export const isIOS = UA && /iphone|ipad|ipod|ios/.test(UA)
1720

1821
let transitionProp
1922
let transitionEndEvent
@@ -49,6 +52,11 @@ export {
4952
animationEndEvent
5053
}
5154

55+
/* istanbul ignore next */
56+
function isNative (Ctor) {
57+
return /native code/.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

6270
export 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

104133
let _Set
105134
/* istanbul ignore if */
106-
if (typeof Set !== 'undefined' && Set.toString().match(/native code/)) {
135+
if (typeof Set !== 'undefined' && isNative(Set)) {
107136
// use native Set when available.
108137
_Set = Set
109138
} else {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy