延迟链崩溃浏览器
Deferred chain crashing browser
这是一个小函数,应该可以打开和关闭一个盒子。打开和关闭需要考虑一些 CSS 转换,所以我想我可以使用 $.Deferred
.
相关代码如下:
function Test(){
// these are assigned Deferred objects during transitions
this.opening = this.closing = false;
this.isOpen = false;
this.x = $('<div />').appendTo('body');
this.x.width();
}
Test.prototype.open = function(){
// box is already opening: return opening deferred
if(this.opening)
return this.opening;
// box is closing: this is the chain
// that is supposed to wait for the box to close,
// then open it again
if(this.closing)
return this.closing.then((function(){
return this.open();
}).bind(this));
// box is already open, resolve immediately
if(this.isOpen)
return $.when();
console.log('opening');
this.opening = new $.Deferred();
this.x.addClass('open');
setTimeout((function(){
this.opening.resolve();
this.opening = false;
this.isOpen = true;
}).bind(this), 1000);
return this.opening;
};
close() 函数是 open() 的反向函数。
当我试图在盒子打开时关闭它时出现问题,反之亦然。例如:
var t = new Test();
t.open(); // takes 1 second
// call close() after 0.05s
setTimeout(function(){
t.close();
}, 50);
似乎发生了堆栈溢出或类似情况。有谁知道是什么原因造成的?
整个测试代码是 here,但具有更高的超时值,因此它不会崩溃 Chrome。
我注意到您的代码存在几个问题:
返回延迟对象而不是 promises,您只能在承诺上执行 .then()
用 bool 值覆盖延迟变量,我正在使用 deferred.state() 而不是
这是您的代码的更新版本:
function Test(){
this.opening = this.closing = false;
this.isOpen = false;
this.x = $('<div />').appendTo('body');
this.x.width();
}
Test.prototype.open = function(){
if(this.opening && this.opening.state() == 'pending')
return this.opening.promise();
if(this.closing && this.closing.state() == 'pending')
return this.closing.promise().then((function(){
return this.open();
}).bind(this));
if(this.isOpen)
return $.when();
console.log('opening');
this.opening = new $.Deferred();
this.x.addClass('open');
setTimeout((function(){
this.isOpen = true;
this.opening.resolve();
}).bind(this), 1000);
return this.opening.promise();
};
Test.prototype.close = function(){
if(this.opening && this.opening.state() == 'pending') {
console.log('opening is pending');
return this.opening.promise().then((function(){
console.log('opening is resolved');
return this.close();
}).bind(this));
}
if(this.closing && this.closing.state() == 'pending'){
console.log('closing is pending');
return this.closing.promise();
}
if(!this.isOpen)
return $.when();
console.log('closing');
this.closing = new $.Deferred();
this.x.removeClass('open');
setTimeout((function(){
console.log('closing resolved');
this.closing.resolve();
this.isOpen = false;
}).bind(this), 1000);
return this.closing.promise();
};
var t = new Test();
t.open();
setTimeout(function(){
t.close();
}, 15);
输出:
"opening"
"opening is pending"
"opening is resolved"
"closing"
"closing resolved"
这里有一个小的计时问题。当您在 .closing
承诺之后链接打开,或在 .opening
承诺之后链接关闭时,这些回调将在承诺被删除之前执行:
this.opening.resolve();
this.opening = false;
问题是 jQuery 确实从 resolve
内部同步执行,因此当 .closing
promise 还在那里,一次又一次地锁住自己……
你应该可以通过
来避免这种情况
var def = this.opening;
this.opening = false;
this.isOpen = true;
def.resolve(); // trigger those who are waiting to immediately close it again
我无法打败 Bergi 对 "stack overflow" 问题的解释,但是我不禁认为你最好不要尝试管理自己的动画队列,这既困难又不必要。
明智地使用 jQuery 的内置 .queue(), .dequeue() and one() 方法,您可以使 CSS 过渡的行为与 jQuery 动画完全一样 - 集成到元素的标准 "fx" 队列完成了 .promise()
方法返回的承诺。
function Test() {
this.x = $('<div/>').appendTo('body');
this.x.width();
this.transitionEndString = 'webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend'; // cross-browser "transitionend" event names.
}
Test.prototype.open = function() {
var that = this,
x = this.x;
x.queue('fx', function(next) {
if(!x.hasClass('open')) {
x.addClass('open').one(that.transitionEndString, next);
} else {
x.dequeue();
}
});
return x.promise();
};
Test.prototype.close = function() {
var that = this,
x = this.x;
x.queue('fx', function(next) {
if(x.hasClass('open')) {
x.removeClass('open').one(that.transitionEndString, next);
} else {
x.dequeue();
}
});
return x.promise();
};
else { x.dequeue(); }
子句是强制承诺在未调用转换时做出响应所必需的。
这是一个小函数,应该可以打开和关闭一个盒子。打开和关闭需要考虑一些 CSS 转换,所以我想我可以使用 $.Deferred
.
相关代码如下:
function Test(){
// these are assigned Deferred objects during transitions
this.opening = this.closing = false;
this.isOpen = false;
this.x = $('<div />').appendTo('body');
this.x.width();
}
Test.prototype.open = function(){
// box is already opening: return opening deferred
if(this.opening)
return this.opening;
// box is closing: this is the chain
// that is supposed to wait for the box to close,
// then open it again
if(this.closing)
return this.closing.then((function(){
return this.open();
}).bind(this));
// box is already open, resolve immediately
if(this.isOpen)
return $.when();
console.log('opening');
this.opening = new $.Deferred();
this.x.addClass('open');
setTimeout((function(){
this.opening.resolve();
this.opening = false;
this.isOpen = true;
}).bind(this), 1000);
return this.opening;
};
close() 函数是 open() 的反向函数。
当我试图在盒子打开时关闭它时出现问题,反之亦然。例如:
var t = new Test();
t.open(); // takes 1 second
// call close() after 0.05s
setTimeout(function(){
t.close();
}, 50);
似乎发生了堆栈溢出或类似情况。有谁知道是什么原因造成的?
整个测试代码是 here,但具有更高的超时值,因此它不会崩溃 Chrome。
我注意到您的代码存在几个问题:
返回延迟对象而不是 promises,您只能在承诺上执行 .then()
用 bool 值覆盖延迟变量,我正在使用 deferred.state() 而不是
这是您的代码的更新版本:
function Test(){
this.opening = this.closing = false;
this.isOpen = false;
this.x = $('<div />').appendTo('body');
this.x.width();
}
Test.prototype.open = function(){
if(this.opening && this.opening.state() == 'pending')
return this.opening.promise();
if(this.closing && this.closing.state() == 'pending')
return this.closing.promise().then((function(){
return this.open();
}).bind(this));
if(this.isOpen)
return $.when();
console.log('opening');
this.opening = new $.Deferred();
this.x.addClass('open');
setTimeout((function(){
this.isOpen = true;
this.opening.resolve();
}).bind(this), 1000);
return this.opening.promise();
};
Test.prototype.close = function(){
if(this.opening && this.opening.state() == 'pending') {
console.log('opening is pending');
return this.opening.promise().then((function(){
console.log('opening is resolved');
return this.close();
}).bind(this));
}
if(this.closing && this.closing.state() == 'pending'){
console.log('closing is pending');
return this.closing.promise();
}
if(!this.isOpen)
return $.when();
console.log('closing');
this.closing = new $.Deferred();
this.x.removeClass('open');
setTimeout((function(){
console.log('closing resolved');
this.closing.resolve();
this.isOpen = false;
}).bind(this), 1000);
return this.closing.promise();
};
var t = new Test();
t.open();
setTimeout(function(){
t.close();
}, 15);
输出:
"opening"
"opening is pending"
"opening is resolved"
"closing"
"closing resolved"
这里有一个小的计时问题。当您在 .closing
承诺之后链接打开,或在 .opening
承诺之后链接关闭时,这些回调将在承诺被删除之前执行:
this.opening.resolve(); this.opening = false;
问题是 jQuery 确实从 resolve
内部同步执行,因此当 .closing
promise 还在那里,一次又一次地锁住自己……
你应该可以通过
来避免这种情况var def = this.opening;
this.opening = false;
this.isOpen = true;
def.resolve(); // trigger those who are waiting to immediately close it again
我无法打败 Bergi 对 "stack overflow" 问题的解释,但是我不禁认为你最好不要尝试管理自己的动画队列,这既困难又不必要。
明智地使用 jQuery 的内置 .queue(), .dequeue() and one() 方法,您可以使 CSS 过渡的行为与 jQuery 动画完全一样 - 集成到元素的标准 "fx" 队列完成了 .promise()
方法返回的承诺。
function Test() {
this.x = $('<div/>').appendTo('body');
this.x.width();
this.transitionEndString = 'webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend'; // cross-browser "transitionend" event names.
}
Test.prototype.open = function() {
var that = this,
x = this.x;
x.queue('fx', function(next) {
if(!x.hasClass('open')) {
x.addClass('open').one(that.transitionEndString, next);
} else {
x.dequeue();
}
});
return x.promise();
};
Test.prototype.close = function() {
var that = this,
x = this.x;
x.queue('fx', function(next) {
if(x.hasClass('open')) {
x.removeClass('open').one(that.transitionEndString, next);
} else {
x.dequeue();
}
});
return x.promise();
};
else { x.dequeue(); }
子句是强制承诺在未调用转换时做出响应所必需的。