all files / vmodel/ transaction.js

91.3% Statements 84/92
85.71% Branches 30/35
100% Functions 9/9
91.3% Lines 84/92
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158    3695×                       686× 351× 335× 335× 335× 351× 351×   335×       335× 335× 362×         1353× 1353×   1348× 1348×             995× 994× 994×     994× 994×   994× 994×   994× 994× 994×   994×           994× 994× 994×         994×           994×       994× 1224× 1224× 1224×       1224× 1224× 1224× 1224× 236×   988× 988×     994× 994× 420×   574× 574× 572×         574× 89× 36×               31× 31× 31× 31× 31× 31×     366×     366× 335× 335×    
import { avalon, config } from '../seed/core'
 
avalon.pendingActions = []
avalon.uniqActions = {}
avalon.inTransaction = 0
config.trackDeps = false
avalon.track = function() {
    Iif (config.trackDeps) {
        avalon.log.apply(avalon, arguments)
    }
}
 
/**
 * Batch is a pseudotransaction, just for purposes of memoizing ComputedValues when nothing else does.
 * During a batch `onBecomeUnobserved` will be called at most once per observable.
 * Avoids unnecessary recalculations.
 */
 
 
export function runActions() {
    if (avalon.isRunningActions === true || avalon.inTransaction > 0)
        return
    avalon.isRunningActions = true
    var tasks = avalon.pendingActions.splice(0, avalon.pendingActions.length)
    for (var i = 0, task; task = tasks[i++];) {
        task.update()
        delete avalon.uniqActions[task.uuid]
    }
    avalon.isRunningActions = false
}
 
 
export function propagateChanged(target) {
    var list = target.observers
    for (var i = 0, el; el = list[i++];) {
        el.schedule(); //通知action, computed做它们该做的事
    }
}
 
//将自己抛到市场上卖
export function reportObserved(target) {
    var action = avalon.trackingAction || null
    if (action !== null) {
 
        avalon.track('征收到', target.expr)
        action.mapIDs[target.uuid] = target;
    }
 
}
 
 
var targetStack = []
 
export function collectDeps(action, getter) {
    if (!action.observers)
        return
    var preAction = avalon.trackingAction
    Iif (preAction) {
        targetStack.push(preAction)
    }
    avalon.trackingAction = action
    avalon.track('【action】', action.type, action.expr, '开始征收依赖项')
        //多个observe持有同一个action
    action.mapIDs = {} //重新收集依赖
    var hasError = true,
        result
    try {
        result = getter.call(action)
        hasError = false
    } finally {
        Iif (hasError) {
            avalon.warn('collectDeps fail', getter + '')
            action.mapIDs = {}
            avalon.trackingAction = preAction
        } else {
            // 确保它总是为null
            avalon.trackingAction = targetStack.pop()
            try {
                resetDeps(action)
            } catch (e) {
                avalon.warn(e)
            }
        }
        return result
    }
 
}
 
 
function resetDeps(action) {
    var prev = action.observers,
        curr = [],
        checked = {},
        ids = []
    for (let i in action.mapIDs) {
        let dep = action.mapIDs[i]
        Eif (!dep.isAction) {
            Iif (!dep.observers) { //如果它已经被销毁
                delete action.mapIDs[i]
                continue
            }
            ids.push(dep.uuid)
            curr.push(dep)
            checked[dep.uuid] = 1
            if (dep.lastAccessedBy === action.uuid) {
                continue
            }
            dep.lastAccessedBy = action.uuid
            avalon.Array.ensure(dep.observers, action)
        }
    }
    var ids = ids.sort().join(',')
    if (ids === action.ids) {
        return
    }
    action.ids = ids
    if (!action.isComputed) {
        action.observers = curr
    } else {
        action.depsCount = curr.length
        action.deps = avalon.mix({}, action.mapIDs)
        action.depsVersion = {};
        for (let i in action.mapIDs) {
            let dep = action.mapIDs[i]
            action.depsVersion[dep.uuid] = dep.version
        }
    }
 
    for (let i = 0, dep; dep = prev[i++];) {
        if (!checked[dep.uuid]) {
            avalon.Array.remove(dep.observers, action)
        }
    }
 
}
 
 
 
function transaction(action, thisArg, args) {
    args = args || []
    var name = 'transaction ' + (action.name || action.displayName || 'noop')
    transactionStart(name)
    var res = action.apply(thisArg, args)
    transactionEnd(name)
    return res
}
avalon.transaction = transaction
 
export function transactionStart(name) {
    avalon.inTransaction += 1;
}
 
export function transactionEnd(name) {
    if (--avalon.inTransaction === 0) {
        avalon.isRunningActions = false
        runActions()
    }
}