-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: further adjust nextTick strategy
fix #6813
- Loading branch information
Showing
6 changed files
with
137 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
/* @flow */ | ||
/* globals MessageChannel */ | ||
|
||
import { noop } from 'shared/util' | ||
import { handleError } from './error' | ||
import { isIOS, isNative } from './env' | ||
|
||
const callbacks = [] | ||
let pending = false | ||
|
||
function flushCallbacks () { | ||
pending = false | ||
const copies = callbacks.slice(0) | ||
callbacks.length = 0 | ||
for (let i = 0; i < copies.length; i++) { | ||
copies[i]() | ||
} | ||
} | ||
|
||
// Here we have async deferring wrappers using both micro and macro tasks. | ||
// In < 2.4 we used micro tasks everywhere, but there are some scenarios where | ||
// micro tasks have too high a priority and fires in between supposedly | ||
// sequential events (e.g. #4521, #6690) or even between bubbling of the same | ||
// event (#6566). However, using macro tasks everywhere also has subtle problems | ||
// when state is changed right before repaint (e.g. #6813, out-in transitions). | ||
// Here we use micro task by default, but expose a way to force macro task when | ||
// needed (e.g. in event handlers attached by v-on). | ||
let microTimerFunc | ||
let macroTimerFunc | ||
let useMacroTask = false | ||
|
||
// Determine (macro) Task defer implementation. | ||
// Technically setImmediate should be the ideal choice, but it's only available | ||
// in IE. The only polyfill that consistently queues the callback after all DOM | ||
// events triggered in the same loop is by using MessageChannel. | ||
/* istanbul ignore if */ | ||
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { | ||
macroTimerFunc = () => { | ||
setImmediate(flushCallbacks) | ||
} | ||
} else if (typeof MessageChannel !== 'undefined' && ( | ||
isNative(MessageChannel) || | ||
// PhantomJS | ||
MessageChannel.toString() === '[object MessageChannelConstructor]' | ||
)) { | ||
const channel = new MessageChannel() | ||
const port = channel.port2 | ||
channel.port1.onmessage = flushCallbacks | ||
macroTimerFunc = () => { | ||
port.postMessage(1) | ||
} | ||
} else { | ||
macroTimerFunc = () => { | ||
setTimeout(flushCallbacks, 0) | ||
} | ||
} | ||
|
||
// Determine MicroTask defer implementation. | ||
// $flow-disable-line, istanbul ignore next | ||
if (typeof Promise !== 'undefined' && isNative(Promise)) { | ||
const p = Promise.resolve() | ||
microTimerFunc = () => { | ||
p.then(flushCallbacks) | ||
// in problematic UIWebViews, Promise.then doesn't completely break, but | ||
// it can get stuck in a weird state where callbacks are pushed into the | ||
// microtask queue but the queue isn't being flushed, until the browser | ||
// needs to do some other work, e.g. handle a timer. Therefore we can | ||
// "force" the microtask queue to be flushed by adding an empty timer. | ||
if (isIOS) setTimeout(noop) | ||
} | ||
} else { | ||
// fallback to macro | ||
microTimerFunc = macroTimerFunc | ||
} | ||
|
||
/** | ||
* Wrap a function so that if any code inside triggers state change, | ||
* the changes are queued using a Task instead of a MicroTask. | ||
*/ | ||
export function withMacroTask (fn: Function): Function { | ||
return fn._withTask || (fn._withTask = function () { | ||
useMacroTask = true | ||
const res = fn.apply(null, arguments) | ||
useMacroTask = false | ||
return res | ||
}) | ||
} | ||
|
||
export function nextTick (cb?: Function, ctx?: Object): ?Promise { | ||
let _resolve | ||
callbacks.push(() => { | ||
if (cb) { | ||
try { | ||
cb.call(ctx) | ||
} catch (e) { | ||
handleError(e, ctx, 'nextTick') | ||
} | ||
} else if (_resolve) { | ||
_resolve(ctx) | ||
} | ||
}) | ||
if (!pending) { | ||
pending = true | ||
if (useMacroTask) { | ||
macroTimerFunc() | ||
} else { | ||
microTimerFunc() | ||
} | ||
} | ||
// $flow-disable-line | ||
if (!cb && typeof Promise !== 'undefined') { | ||
return new Promise(resolve => { | ||
_resolve = resolve | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters