소스 검색

warn against infinite update loops

Evan You 11 년 전
부모
커밋
9013fbbe7f
2개의 변경된 파일35개의 추가작업 그리고 2개의 파일을 삭제
  1. 16 2
      src/batcher.js
  2. 19 0
      test/unit/specs/batcher_spec.js

+ 16 - 2
src/batcher.js

@@ -1,4 +1,5 @@
 var _ = require('./util')
+var MAX_UPDATE_COUNT = 10
 
 // we have two separate queues: one for directive updates
 // and one for user watcher registered via $watch().
@@ -61,7 +62,21 @@ function run (queue) {
  */
 
 exports.push = function (job) {
-  if (!job.id || !has[job.id] || flushing) {
+  var id = job.id
+  if (!id || !has[id] || flushing) {
+    if (!has[id]) {
+      has[id] = 1
+    } else {
+      has[id]++
+      // detect possible infinite update loops
+      if (has[id] > MAX_UPDATE_COUNT) {
+        _.warn(
+          'You may have an infinite update loop for the ' +
+          'watcher with expression: "' + job.expression + '".'
+        )
+        return
+      }
+    }
     // A user watcher callback could trigger another
     // directive update during the flushing; at that time
     // the directive queue would already have been run, so
@@ -71,7 +86,6 @@ exports.push = function (job) {
       return
     }
     ;(job.user ? userQueue : queue).push(job)
-    has[job.id] = job
     if (!waiting) {
       waiting = true
       _.nextTick(flush)

+ 19 - 0
test/unit/specs/batcher_spec.js

@@ -1,3 +1,4 @@
+var _ = require('../../../src/util')
 var batcher = require('../../../src/batcher')
 var nextTick = require('../../../src/util').nextTick
 
@@ -7,6 +8,7 @@ describe('Batcher', function () {
 
   beforeEach(function () {
     spy = jasmine.createSpy('batcher')
+    spyOn(_, 'warn')
   })
   
   it('push', function (done) {
@@ -80,4 +82,21 @@ describe('Batcher', function () {
     })
   })
 
+  it('warn against infinite update loops', function (done) {
+    var count = 0
+    var job = {
+      id: 1,
+      run: function () {
+        count++
+        batcher.push(job)
+      }
+    }
+    batcher.push(job)
+    nextTick(function () {
+      expect(count).not.toBe(0)
+      expect(_.warn).toHaveBeenCalled()
+      done()
+    })
+  })
+
 })