美文网首页
自制前端框架Day21 $evalAsync的实现

自制前端框架Day21 $evalAsync的实现

作者: 蚊子爸爸 | 来源:发表于2017-06-27 16:17 被阅读26次

    $evalAsync是什么

    顾名思义,延迟执行一段作用于相应scope的eval代码。于timeout的区别是,timeout会把代码交给浏览器去执行,开发者无法控制到底什么时候执行。$evalAsync运行代码是在digest周期内运行的。具体可以通过这个测试案例看的很明白。

      
       it('$evalAsync', function() {
           scope.aValue = 123;
           scope.asyncEvaluated = false;
           scope.asyncEvaluatedImmediately = false;
           var watchFn = function(scope){
               return scope.aValue;
           }
           var listenFn = function(newvalue,oldvalue,scope){
               scope.$evalAsync(function(scope){
                   scope.asyncEvaluated = true;//随后执行的
               })
               scope.asyncEvaluatedImmediately = scope.asyncEvaluated;//立即执行的,这时候应该是false
           }
           scope.$watch(watchFn,listenFn);
           scope.$digest();
    
           expect(scope.asyncEvaluated).toBe(true);
           expect(scope.asyncEvaluatedImmediately).toBe(false);
       });
    

    实现思路

    首先在scope里面创建一个延迟eval队列。

    function Scope() {
        this.$$watchers = [];
        this.$$lastDirtyWatch = null;
        this.$$asyncQueue = [];
    }
    

    调用$evalAsync的时候就把要执行的函数推入这个队列。

    Scope.prototype.$evalAsync = function (expr) {
        this.$$asyncQueue.push({ scope: this, expression: expr });
    }
    

    在每次digest之前都把这个队列里的函数用当前scope执行一下

    
    Scope.prototype.$digest = function () {
        var dirty;
        var ttl = 10;
        do {
            while (this.$$asyncQueue.length) {
                var asyncTask = this.$$asyncQueue.shift();
                asyncTask.scope.$eval(asyncTask.expression);
            }
            dirty = this.$$digestOnce();
            if (dirty && !(ttl--)) {
                throw "digest次数到达上限依然不稳定"
            }
        } while (dirty);
    }
    

    为什么这个while循环放在了digestOnce的前面?

    注意的是,这里的while循环放到了$$digestOnce方法的前面,需要解释一下为什么放到了前面:
    第一次执行这个digest的时候,是初始化的时候,值肯定是脏的,这时候$$asyncQueue里面是没有东西的,所以直接运行$$digestOnce,运行$$digestOnce的时候,才会执行watcher里面的listenFn,也就是这时候才会把asyncEval注册的方法推入队列,然后scope.asyncEvaluatedImmediately = scope.asyncEvaluated;这段代码会运行,当然是false的。
    运行完了以后,跳出digestOnce,因为linstenFn被运行了,所以肯定返回的是dirty,那么就会进行下一次循环,在这次新的循环里,再去运行async队列里面的函数。

    如果放在digestOnce后面会怎么样?

    详情可以看自己录的自言自语的视频。

    相关文章

      网友评论

          本文标题:自制前端框架Day21 $evalAsync的实现

          本文链接:https://www.haomeiwen.com/subject/qyqjcxtx.html