(Promise loop)_让div一个一个高亮有延迟

(Promise loop) _ Make divs highlight one by one with a delay

我正在尝试做一个小例子,其中四个div将依次高亮显示。我想用循环和承诺来做,但页面进入无限循环。我的承诺肯定有问题,但我想不通我应该怎么做。

'user strict';
var divAr = document.getElementsByTagName("div");
console.log(divAr);
var colors = ['blue', 'red', 'yellow', 'green'];
var i = 0;
while (i < divAr.length) {
  divAr[i].style.backgroundColor = colors[i];
  var promise = new Promise(function(resolve) {
    lightenButton(i);
    window.setTimeout(function() {
      resolve(i);
    }, 1500);
  })
  promise.then(function(i) {
    i++;
  });
}

function lightenButton(index) {
  var oldColor = window.getComputedStyle(divAr[index], null)['background-color'];
  var newColor = "";

  function setOldColor() {
    divAr[index].style.backgroundColor = oldColor;
  }

  switch (oldColor) {
    case "rgb(0, 0, 255)":
      newColor = "#9999ff";
      break;
    case "rgb(255, 0, 0)":
      newColor = "#ff9999";
      break;
    case "rgb(255, 255, 0)":
      newColor = "#ffffcc";
      break;
    case "rgb(0, 128, 0)":
      newColor = "#99ff99";
      break;
  }
  divAr[index].style.backgroundColor = newColor;
  setTimeout(setOldColor, 500);
}
body {
  background-color: Grey;
}
div {
  width: 80px;
  height: 55px;
  border: 1px dotted grey;
  margin: 10px;
  float: left;
  text-align: center;
  line-height: 50px;
}
<div id="1">1</div>
<div id="2">2</div>
<div id="3">3</div>
<div id="4">4</div>

你可以这样做:

var divs = [].slice.call(divAr, 0);
divs.reduce(function(promise, div, index) {
    return promise.then(function() {
        lightenButton(index);
        return new Promise(function(resolve, reject) {
            setTimeout(function() {
                resolve();
            }, 1500);
        });
    });
}, Promise.resolve());

那么,它是如何工作的?我们先来了解一下我们需要构建的promise链:

startpromise.then(lightenButtonIndex1).then(lightenButtonIndex2).then(...

Reduce 遍历元素数组,returns 是前一个函数执行的结果。正如您从上面所示的链中看到的那样,我们需要在每次迭代中都使用 promise returned 以便我们可以使用 then 方法将我们的函数附加到它,然后再次 return promise下一次迭代。这正是代码在做什么。所以,我们从 startpromise 开始,它是在代码中用 Promise.resolve() 创建的。然后迭代:

迭代 1

params (startpromise, div1, index=0)

添加回调和return承诺

var lightenButtonIndex1Promise = startpromise.then(lightenButtonIndex1);
return lightenButtonIndex1Promise;

迭代 2

params (lightenButtonIndex1Promise, div2, index=1)

添加回调和return承诺

var lightenButtonIndex2Promise = lightenButtonIndex1Promise .then(lightenButtonIndex2);
return lightenButtonIndex2Promise;

所以它以相同的方式进行迭代 4。


这里是没有reduce的版本,但对我来说看起来很麻烦:

var promise = Promise.resolve();

for (var i=0; i< divs.length; i++) {
    promise = promise.then((function(index) {
        return function() {
            lightenButton(index);
            return new Promise(function(resolve, reject) {
                setTimeout(function() {
                    resolve();
                }, 1500);
            });
        }
    })(i));
}

这是工作示例,我修改了您的 lightenButton 函数以进行演示:

document.addEventListener('DOMContentLoaded', function () {
    var divAr = document.getElementsByTagName("div");

    var divs = [].slice.call(divAr, 0);

    divs.reduce(function (promise, div, index) {
        return promise.then(function () {
            lightenButton(index);
            return new Promise(function (resolve, reject) {
                setTimeout(function () {
                    resolve();
                }, 1500);
            });
        });
    }, Promise.resolve());

    function lightenButton(index) {
        divs.forEach(function (div, i) {
            if (index === i) {
                divAr[i].style.backgroundColor = "blue";
            } else {
                divAr[i].style.backgroundColor = "";
            }

        });
    }
});
body {
  background-color: Grey;
}

div {
  width: 80px;
  height: 55px;
  border: 1px dotted grey;
  margin: 10px;
  float: left;
  text-align: center;
  line-height: 50px;
}
<div id="1">1</div>
<div id="2">2</div>
<div id="3">3</div>
<div id="4">4</div>

这可能就是您要找的。它几乎不需要任何解释,因为 async/await 使您的代码非常简单。显然,是的,您需要 transpile 您的 JavaScript 才能在大多数浏览器中运行。

const wait = ms => new Promise(r => setTimeout(r, ms));

async function loop (elems, i) {
  elems[i].classList.add('highlight');
  await wait(1000);
  elems[i].classList.remove('highlight');
  await wait(1000);
  return await loop(elems, (i + 1) % elems.length);
}

loop(document.querySelectorAll('div'), 0);

这是一个在浏览器中运行的转译版本。展开代码段并查看它的工作情况:)

'use strict';

var loop = function () {
  var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee(elems, i) {
    return regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            elems[i].classList.add('highlight');
            _context.next = 3;
            return wait(1000);

          case 3:
            elems[i].classList.remove('highlight');
            _context.next = 6;
            return wait(1000);

          case 6:
            _context.next = 8;
            return loop(elems, (i + 1) % elems.length);

          case 8:
            return _context.abrupt('return', _context.sent);

          case 9:
          case 'end':
            return _context.stop();
        }
      }
    }, _callee, this);
  }));

  return function loop(_x, _x2) {
    return _ref.apply(this, arguments);
  };
}();

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

var wait = function wait(ms) {
  return new Promise(function (r) {
    return setTimeout(r, ms);
  });
};

loop(document.querySelectorAll('.panel'), 0);
body {
  display: flex;
  flex-direction: row;
  justify-content: space-around;
  align-items: center;
}

.panel {
  width: 3rem;
  background-color: #eee;
  text-align: center;
  padding: 0.5rem 1rem;
  transition: background-color 0.5s ease;
}

.highlight {
  background-color: dodgerblue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.16.0/polyfill.js"></script>
<div class="panel">1</div>
<div class="panel">2</div>
<div class="panel">3</div>
<div class="panel">4</div>