我有一个函数foo
,我想添加一个睡眠/等待函数来制作一种DOM元素动画。我已经做了一些研究,我知道暂停一个javascript函数是不可能的,因为它会冻结浏览器 - 如果我错了,请纠正我。我怎样才能克服它?
function foo() {
while (someCondition) {
var $someDiv = $('.someDiv:nth-child(' + guess + ')');
$someDiv.css({'background-color': 'red'});
wait 1000ms
$someDiv.css({'background-color': 'blue'});
wait 1000ms
if (someCondition2) {
doSomething; }
else {
for loop }
}
}
$someDiv
引用每个while
循环迭代的不同DOM元素,因为变量guess
更改
我尝试了什么
我使用下面的函数并且它有效,但问题是我无法在异步函数foo中使用for
循环
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
我尝试setTimeout
,但我无法获得任何有效结果。
如果我将这段代码包裹在setTimeout
中:
('$someDiv').css({'background-color': 'red'});
然后在指定的时间后,所有$someDiv's
一起更改css样式(请记住,$someDiv
引用每个while
循环迭代的不同DOM元素)。
如果我使用setTimeout
,if
语句将else
包含在一段代码中,那么我就会收到错误 - 无限循环
问题
简化foo功能只是为了可视化问题。我正在处理的原始函数可以在codepen(findNumber
函数)
我想制作二进制搜索算法动画。类似于this
的东西如何达到预期效果?
一般情况下:如何在每次迭代之间有间隔的循环中为DOM元素设置动画?
答案 0 :(得分:2)
您可能想要检查jQuery queue()
(fiddle):
$("div#someDiv")
.queue(function() {
console.log('step 1');
$(this).css({
'background-color': 'blue'
}).dequeue();
})
.delay(800)
.queue(function() {
console.log('step 1');
$(this).css({
'background-color': 'red'
}).dequeue();
})
你也可以玩Settimeout(fiddle):
var steps = [
function() {
$('#someDiv').css({
'background-color': 'red'
});
},
function() {
$('#someDiv').css({
'background-color': 'orange'
});
},
function() {
$('#someDiv').css({
'background-color': 'yellow'
});
},
function() {
$('#someDiv').css({
'background-color': 'green'
});
},
function() {
$('#someDiv').css({
'background-color': 'blue'
});
}
];
(function(count) {
if (count < 5) {
steps[count]();
var caller = arguments.callee;
window.setTimeout(function() {
caller(count + 1);
}, 1000);
}
})(0);
答案 1 :(得分:2)
这个问题最好,最干净的解决方案是async
/await
feature,它将出现在未来版本的Javascript(ES2017)中。这可以让你摆脱回调地狱。您可以创建一个简单的sleep
函数,如下所示:
function sleep(time) {
return new Promise(resolve => setTimeout(()=>resolve(), time));
}
您可以使用正常的Promise
处理:
sleep(1000).then(()=>console.log('A second later'));
但是,使用async
功能,您可以使用await
关键字使代码等待承诺在继续之前得到解决。
async function doSomething() {
await sleep(1000);
console.log('A second later');
}
这意味着您也可以使用普通循环,包括break
和continue
语句:
async function doSomething() {
let i = 0;
while (true) {
await sleep(1000);
console.log(i);
if (++i === 5) break;
}
}
这意味着您的代码可以大大简化:
async function foo() {
var n = 5;
while (n > 0) {
n--;
var wait = 0;
//$('#someDiv').css({'background-color': 'red'});
console.log('doSomething-1');
//wait 1000ms
await sleep(1000);
//$('#someDiv').css({'background-color': 'blue'});
console.log('doSomething-2');
//wait 1000ms
await sleep(1000);
if (true) {
console.log('doSomething-3');
break;
} else {
console.log('loop')
}
}
}
(jsFiddle)
唯一的问题是此功能远非普遍支持。因此,您需要使用Babel等软件进行转换。
另请注意,在幕后,您的foo
功能现在立即返回并提供Promise
。使用函数的返回值解析Promise
。因此,如果您希望在foo
完成后执行更多代码,则必须执行foo().then(/*callback*/)
。
答案 2 :(得分:1)
您可以使用jQuery queue(),dequeue(),delay()
来执行此操作但是,由于你有closures inside loop,你必须使用bind()将每次迭代的变量绑定到排队的函数中。
这里有body
的队列,每个guess
都有一个队列,每个猜测的队列都在队列出列队列中出队。循环内部的循环。恕我直言,可以有更简单的方法,这只是为了对队列,延迟和绑定提出一个想法。
我对您的代码进行了一些小的更改,只是为了让演示工作。
$(document).ready(function() {
var body = $('body');
//buttons click handler functions
$('#generateButton').on("click", generateArray);
$('#findButton').on("click", findNumber);
//enable calling functions by 'enter' key
$('#generateInput').keypress(function(e) {
if (e.which == 13) {
generateArray();
}
});
$('#findInput').keypress(function(e) {
if (e.which == 13) {
findNumber();
}
});
//functions
function generateArray() {
//variables
var $generateGroup = $('.generate');
var $generateInput = $('#generateInput');
var $generateInputVal = $generateInput.val();
var $generateButton = $('#generateButton');
var $findInput = $('#findInput');
var $findButton = $('#findButton');
var $arraySection = $('.array-section');
//validation
if ($.isNumeric($generateInputVal) && $generateInputVal >= 10 && $generateInputVal <= 100) {
//set styling if success
$generateGroup.removeClass('has-error');
$generateButton.removeClass('error');
$generateGroup.addClass('has-success');
$generateButton.addClass('success');
//disable generate input group
$generateInput.prop('disabled', true);
$generateButton.prop('disabled', true);
//enable find input group
$findInput.prop('disabled', false);
$findButton.prop('disabled', false);
//clear array section
$arraySection.empty();
//generate array = create divs and append them to array section
for (var i = 0; i < $generateInputVal; i++) {
var $number = $('<div>', {
'class': 'number'
});
$arraySection.append($number.text(i + 1));
}
} else {
// set styling if error
$generateGroup.removeClass('has-success');
$generateButton.removeClass('success');
$generateGroup.addClass('has-error');
$generateButton.addClass('error');
}
}
function findNumber() {
//variables
var animationSpeed = 5000;
var animationCut = 1000;
var $generateInput = $('#generateInput');
var $generateInputVal = $generateInput.val();
var $findInput = $('#findInput');
var $findInputVal = $findInput.val();
var min = 0;
var max = parseInt($generateInputVal);
var guess;
var n = 0;
var guesses = [];
var rejected;
// --- binary search loop ---
while (max >= min) {
n++;
//compute guess as the average of max and min
guess = Math.ceil((min + max) / 2);
console.log("GUESS",guess);
//guessed number animation
var $guessNumber = $('.number:nth-child(' + guess + ')');
console.log(min + ' ' + max + ' ' + guess);
$guessNumber.queue('guessNum', function(next) {
$(this).css({
'background-color': '#000000',
'color': '#ffffff',
'font-weight': 'bold'
});
next();
});
$guessNumber.delay(animationCut, 'guessNum');
//await sleep(animationSpeed);
//var myVar = setInterval(function(){
$guessNumber.queue('guessNum', function(next) {
$(this).css({
'background-color': 'white',
'color': 'black',
'border': '3px solid #000000'
});
next()
});
$guessNumber.delay(animationCut, 'guessNum');
body.queue('guessNumbers', function(){
console.log('guessNumbers');
$(this).dequeue('guessNum');
body.dequeue('guessNumbers');
}.bind($guessNumber));
//await sleep(animationSpeed);
//if guessed number equals find number then stop
if (guess === parseInt($findInputVal)) {
//found number animation
$guessNumber.queue('guessNum', function(next) {
console.log('GOT RESULT', this);
$(this).css({
'color': '#3c763d',
'background-color': '#dff0d8',
'border': '3px solid #3c763d'
});
next();
});
body.dequeue('guessNumbers');
break;
}
//if guessed nsumber is to low, set new min value
else if (guess < parseInt($findInputVal, 10)) {
rejected = $('.number').slice(min, guess);
min = guess + 1;
}
//if guessed number is to high, set new max value
else if(guess > parseInt($findInputVal, 10)) {
rejected = $('.number').slice(guess, max);
max = guess - 1;
}
body.queue('guessNumbers',function(){
console.log("rejected",rejected);
this.css({backgroundColor: 'red'});
body.dequeue('guessNumbers');
}.bind(rejected)).delay(animationSpeed, 'guessNumbers');
}
}
});
// function sleep(ms) {
// return new Promise(resolve => setTimeout(resolve, ms));
// }
html,
body {
margin: 0 auto;
box-sizing: border-box;
}
.section {
margin-top: 40px;
margin-bottom: 40px;
}
.array-section {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
}
.input-group {
margin: 5px;
}
.number {
display: flex;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
text-align: center;
margin: 5px;
padding: 5px;
border: 1px solid gray;
border-radius: 3px;
transition: all 0.8s;
}
.error {
background: rgb(202, 60, 60);
color: white;
border: 1px solid rgb(202, 60, 60);
transition: 0.5s;
}
.success {
background: rgb(28, 184, 65);
color: white;
border: 1px solid rgb(28, 184, 65);
transition: 0.5s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container text-center">
<div class="section section-title">
<h1>BINARY SEARCH</h1>
</div>
<div class="section input-section">
<div class="col-md-4 col-md-offset-2 col-sm-6 col-sm-offset-3 col-xs-8 col-xs-offset-2">
<div class="input-group generate">
<input type="text" class="form-control input-lg" placeholder="10 - 100" id="generateInput">
<div class="input-group-btn">
<button class="btn btn-default btn-lg" id="generateButton">
Generate array
</button>
</div>
</div>
</div>
<div class="col-md-4 col-md-offset-1 col-sm-6 col-sm-offset-3 col-xs-8 col-xs-offset-2">
<div class="input-group">
<input type="text" class="form-control input-lg" placeholder="1 - 100" id="findInput" disabled>
<div class="input-group-btn">
<button class="btn btn-default btn-lg" type="submit" id="findButton" disabled>
Find number
</button>
</div>
</div>
</div>
</div>
<div class="col-xs-12 section array-section">
<div class="number">1</div>
<div class="number">2</div>
<div class="number">3</div>
<div class="number">...</div>
<div class="number">n</div>
</div>
</div>
答案 3 :(得分:0)
我制作一个简单的代码...也许可以帮到你:
function foo() {
while (true) {
var wait = 0;
$('#someDiv').css({'background-color': 'red'});
//wait 1000ms
wait = wait + 1000;
setTimeout(function(){
$('#someDiv').css({'background-color': 'blue'});
}, wait);
//wait 1000ms
wait = wait + 1000;
setTimeout(function(){
if (true) {
console.log('doSomething');
}
else {
console.log('loop')
}
}, wait);
break;
}
}
foo();
秘密在&#34;等待&#34;,这个值将是你想要的时间和最后一个&#34;等待&#34;的总和。值。您将获得预期的结果,但另一种方法。
我希望我有所帮助。
答案 4 :(得分:0)
我更正了安德烈罗德里格斯的代码:
var nbCall = 0;
function foo() {
var wait = 0;
$('#someDiv').css({'background-color': 'red'});
//wait 1000ms
wait = wait + 1000;
setTimeout(function(){
$('#someDiv').css({'background-color': 'blue'});
}, wait);
//wait 2000ms
wait = wait + 1000;
setTimeout(function(){
if (nbCall++ > 5) { // Are other cond
console.log('doSomething');
}
else {
foo();
}
}, wait);
}
foo();
jsFiddle:https://jsfiddle.net/nnf8vcko/