异步事件优先级:Promise > MutationObserver > setImmediate > setTimeout

  1. // 是否使用了微任务来处理异步更新
  2. export let isUsingMicroTask = false
  3. // 更新队列
  4. const callbacks = []
  5. // 是否正在等待开启更新中,当 pending 为 false 时,意味着异步更新队列已经为空,并且下一个事件循环没有更新任务
  6. let pending = false
  7. // 更新函数
  8. // 一旦执行更新,则直接清空队列,并取其浅拷贝进行更新
  9. function flushCallbacks () {
  10. pending = false
  11. const copies = callbacks.slice(0)
  12. callbacks.length = 0
  13. for (let i = 0; i < copies.length; i++) {
  14. copies[i]()
  15. }
  16. }
  17. // 异步函数
  18. let timerFunc
  19. // 1. 如果当前环境有 Promise,则使用 Promise 当异步更新函数
  20. // 2. 如果不是 IE 且支持 MutationObserver,则使用 MutationObserver 的回调函数作为异步更新函数
  21. // 3. 如果支持 setImmediate,则使用 setImmediate 作为异步更新函数
  22. // 4. 最下策,使用 setTimeout 作为一部更新函数
  23. if (typeof Promise !== 'undefined' && isNative(Promise)) {
  24. const p = Promise.resolve()
  25. timerFunc = () => {
  26. p.then(flushCallbacks)
  27. // 再 IOS webview,会出现一些奇怪的情况,级微任务队列未刷新,解决这个问题的方法就是注册一个空的宏任务
  28. if (isIOS) setTimeout(noop)
  29. }
  30. isUsingMicroTask = true
  31. } else if (!isIE && typeof MutationObserver !== 'undefined' && (
  32. isNative(MutationObserver) ||
  33. MutationObserver.toString() === '[object MutationObserverConstructor]'
  34. )) {
  35. let counter = 1
  36. const observer = new MutationObserver(flushCallbacks)
  37. const textNode = document.createTextNode(String(counter))
  38. observer.observe(textNode, {
  39. characterData: true
  40. })
  41. timerFunc = () => {
  42. counter = (counter + 1) % 2
  43. textNode.data = String(counter)
  44. }
  45. isUsingMicroTask = true
  46. } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  47. timerFunc = () => {
  48. setImmediate(flushCallbacks)
  49. }
  50. } else {
  51. // Fallback to setTimeout.
  52. timerFunc = () => {
  53. setTimeout(flushCallbacks, 0)
  54. }
  55. }
  56. export function nextTick (cb?: Function, ctx?: Object) {
  57. let _resolve
  58. // 将回调函数加入队列
  59. callbacks.push(() => {
  60. if (cb) {
  61. try {
  62. cb.call(ctx)
  63. } catch (e) {
  64. handleError(e, ctx, 'nextTick')
  65. }
  66. } else if (_resolve) {
  67. _resolve(ctx)
  68. }
  69. })
  70. // 如果当前是第一个入队的,则开启异步更新,并将 pending 置否
  71. if (!pending) {
  72. pending = true
  73. timerFunc()
  74. }
  75. // 如果没有 cb,并且当前环境支持 Promise,则返回一个 Promise,resolve(ctx)
  76. if (!cb && typeof Promise !== 'undefined') {
  77. return new Promise(resolve => {
  78. _resolve = resolve
  79. })
  80. }
  81. }

参考