Weex开发技巧前端weex社区

Vue2.0中的异步队列

2017-03-21  本文已影响233人  cpu_driver

在vue的体系中,数据的双向绑定主要通过<code>Object.defineProperty</code>进行数据从模型到视图的流动及通过DOM事件进行数据从视图到模型的流动。这些都比较容易理解,但是如果频繁的更改模型的话,就会造成频繁的更新View,会造成频繁的dom操作,这是比较耗时的,因此需要进行优化,Vue2.0是通过批处理进行优化的,而异步执行队列,是批处理中一个很重要的一个环节。

在vue.js(Vue.js v2.1.10)416-490行,描述了这一神奇的异步执行队列。下面我们将进行相关的代码分析。

var nextTick =(function(){
  return function queueNextTick (cb, ctx) {}
})()

我们可以看到整体来说,这是一个由立即执行函数构成的,返回值是函数的一个函数,我们可以使用下面的测试代码

var nextTick =(function(){
return function queueNextTick (cb, ctx) {
  console.log(cb);
}
})();
nextTick("this is test1")
var nextTick = (function () {
    var callbacks = [];
    var pending = false;
    var timerFunc;

    function nextTickHandler () {
    }
  if (typeof Promise !== 'undefined' && isNative(Promise)) {
    var p = Promise.resolve();
    var logError = function (err) { console.error(err); };
    timerFunc = function () {
      p.then(nextTickHandler).catch(logError);
      if (isIOS) { setTimeout(noop); }
    };
  } else if (typeof MutationObserver !== 'undefined' && (
    isNative(MutationObserver) ||
    MutationObserver.toString() === '[object MutationObserverConstructor]'
  )) {
    var counter = 1;
    var observer = new MutationObserver(nextTickHandler);
    var textNode = document.createTextNode(String(counter));
    observer.observe(textNode, {
      characterData: true
    });
    timerFunc = function () {
  }
  return function queueNextTick (cb, ctx) {
  }
})();

我们看到在函数内部,有三个函数,nextTickHandler,timerFunc,以及queueNextTick 。
其中

if(typeof Promise !== 'undefined' && isNative(Promise)){
}
else if(typeof MutationObserver !== 'undefined' && (
  isNative(MutationObserver) ||
  MutationObserver.toString() === '[object MutationObserverConstructor]'){
}else{
}

是用来判断当前执行环境,给timerFunc 选择一个合适的执行条件。如果是支持Promise的环境中,timerFunc 中则用<code>p.then(nextTickHandler)</code>和 <code>setTimeout(nextTickHandler, 0);</code>执行回调;如果是MutationObserver环境则通过修改文本节点的值的变化触发调用的的。关于为什么优先使用Promise,知乎里也在讨论这件事,点这里看详情。

function queueNextTick (cb, ctx) {
    var _resolve;
    callbacks.push(function () {
      if (cb) { cb.call(ctx); }
      if (_resolve) { 
          console.log(0);
           _resolve(ctx); }
    });
    if (!pending) {
      pending = true;
      timerFunc();
    }
    if (!cb && typeof Promise !== 'undefined') {
      return new Promise(function (resolve) {
          console.log("now")
        _resolve = resolve;
      })
    }
  }

当我们使用<code>nextTick(function Test(){console.log("1" +this.isIOS);},this)</code>时,我们可以发现 Test封装后被放进了回调队列callbacks中,

if (cb) { cb.call(ctx); }
if (_resolve) { 
       console.log(0);
        _resolve(ctx); }

注意这里的_resolve,他是在

if (!cb && typeof Promise !== 'undefined') {
   return new Promise(function (resolve) {
     _resolve = resolve;
   })
 }

里面进行了赋值,因为promise注册函数是立即执行的,因此在返回前给当前的域中的_resolve赋了值。当进行回调时,便可以使用_resolve的值。我们可以使用执行空函数进行测试:

console.log(nextTick();)//打印是Promise的相关内容 
function nextTickHandler () {
    pending = false;
    //对callbacks进行深复制
    var copies = callbacks.slice(0);
    callbacks.length = 0;
    for (var i = 0; i < copies.length; i++) {
        if(i==0){
            copies[i]();
        }
      copies[i]();
    }
  }

此函数便是在触发时把回调队列中函数依次调用,特别注意 pending=flase这句话用来把执行回调过程中新加进回调数组的计入下一个tick,另外需要注意<code> var copies =callbacks.slice(0); callbacks.length = 0;
</code>这句话,用的很好,首先对回掉数组进行深复制,然后把callbacks置空。之所以不用callbacks=[],是因为设置length=0可以不改变原有对象的地址。

nextTick().then(()=>{
  console.log("last");
})
var ss={
    isIOS:"sss"
}
nextTick(function(){
    console.log("1" +this.isIOS);
},ss)

最后我画出了队列图

队列执行图
上一篇下一篇

猜你喜欢

热点阅读