vue principle

nextTick

MessageChannel

1
2
3
4
5
6
7
8
9
10
11
12
var channel = new MessageChannel();
var port1 = channel.port1;
var port2 = channel.port2;
port1.onmessage = function(event) {
console.log("port1收到来自port2的数据:" + event.data);
}
port2.onmessage = function(event) {
console.log("port2收到来自port1的数据:" + event.data);
}

port1.postMessage("发送给port2");
port2.postMessage("发送给port1");

nextTick

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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]()
}
}

let microTimerFunc
let macroTimerFunc
let useMacroTask = false

if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
macroTimerFunc = () => {
setImmediate(flushCallbacks)
}
} else if (typeof MessageChannel !== 'undefined' && (
isNative(MessageChannel) ||
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)
}
}

if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
microTimerFunc = () => {
p.then(flushCallbacks)
if (isIOS) setTimeout(noop)
}
} else {
// fallback to macro
microTimerFunc = macroTimerFunc
}

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) {
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()
}
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
  • 对外暴露了 2 个函数,withMacroTask,nextTick

  • macro task 的实现,检测是否支持 setImmediate(IE 和 Edge支持),否则检测 MessageChannel,最后 setTimeout 0;

  • micro task 的实现,检测是否支持 Promise,否则指向 macro task 的实现

  • 把传入的回调函数 cb 压入 callbacks 数组,根据 useMacroTask 条件执行 macroTimerFunc 或 microTimerFunc

  • macroTimerFunc 或 microTimerFunc 内的逻辑会在下一个时间循环执行 flushCallbacks,callbacks 遍历并执行回调函数

  • 使用 callbacks 而不直接在 nextTick 中执行回调函数,是为了保证在同一个 tick 内多次执行 nextTick,不会开启多个异步任务,而把这些异步任务都压成一个同步任务

nextTick 不传 cb 参数,优化

1
2
3
4
5
 if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}

当 _resolve 函数执行,就会跳到 then 的逻辑中

withMacroTask

  • 它是对函数做一层包装,确保函数执行过程中对数据任意的修改,触发变化执行 nextTick 的时候强制走 macroTimerFunc

  • 比如对于一些 DOM 交互事件,如 v-on 绑定的事件回调函数的处理,会强制走 macro task

返回
顶部