GeorGios
GeorGios

Reputation: 167

Javascript: Promise() .then doesnt work

I am trying to make a loader animation style for my website. The loading animation is 16 bars which increase and decrease in order. For example, the first bar will increase and then decrease back to its original size. Then the next bar will repeat this process until all bars have done it, then stop the process and reveal the page. In this case, because JavaScript is asynchronous on calling functions, I used a promise to get around it. The use of promise is to animate the bar after the previous has completed animating. Currently, my code animates only the first bar and stops there. It doesn't continue to animate the rest of them. Below are all of my codes:

IMPORTANT! The issue it on javascript. Don't spend time on HTML or CSS.

var index = -1;

function loading(){
	var loader = document.getElementById("loader");
	display = window.getComputedStyle(loader).display;
	if (display == "block"){
		var child = document.getElementById("loader-ul").getElementsByTagName("div");
		index = index + 1;
		alert("dd");
		animate(child);
	}
}

function animate(element){
	var el = element[index];
	var MaxHeight = false;
	var finished = false;
	function anim(){
		return new Promise(function(resolve, reject){
			if (finished == false){
				if (MaxHeight == false){
					var height = parseInt(window.getComputedStyle(el).height.slice(0, -2));
					var Bot = parseFloat(window.getComputedStyle(el).bottom.slice(0, -2));
					height = height + 1;
					Bot = Bot + 0.5;
					el.style.bottom = Bot + "px";
					el.style.height = height + "px";
					if (height <= 100){
						window.requestAnimationFrame(anim);
					}
					else{
						MaxHeight = true;
					}
				}
				if (MaxHeight == true){
					var height = parseInt(window.getComputedStyle(el).height.slice(0, -2));
					var Bot = parseFloat(window.getComputedStyle(el).bottom.slice(0, -2));
					height = height - 1;
					Bot = Bot - 0.5;
					el.style.bottom = Bot + "px";
					el.style.height = height + "px";
					if (height >= 50){
						window.requestAnimationFrame(anim);
					}
					else{
						MaxHeight = true;
						finished = true;
						el.style.bottom = 0 + "px";
						el.style.height = 50 + "px";
					}
				}
			}
			else{
				resolve();
			}
		});
	}
	anim().then(loading);
}
body{
	margin: 0px;
	padding: 0px;
	margin: auto;
}

#loader{
	display: block;
	position: fixed;
	height: 100%;
	width: 100%;
	background-color: white;
	z-index: 9999;
}

#loader .center{
	position: relative;
	height: 50px;
	width: 200px;
	background-color: red;
	top: 50%;
	transform: translateY(-50%);
	margin: auto;
}

#loader .center div{
	width: 2px;
	height: 50px;
	background-color: blue;
	float: left;
	padding: 0px;
	margin-right: 5px;
	margin-bottom: 25px;
	position: relative;
}
<body onload="loading()">
    <div id="loader">
			<div class="center" id="loader-ul">
				<div></div>
				<div></div>
				<div></div>
				<div></div>
				<div></div>
				<div></div>
				<div></div>
				<div></div>
				<div></div>
				<div></div>
				<div></div>
				<div></div>
				<div></div>
				<div></div>
				<div></div>
				<div></div>
			</div>
		</div>
	</body>

I also have a link to jsfiddle:

https://jsfiddle.net/6227jjen/

Thank you all!

Note: I have added some alerts for debugging purposes.

Upvotes: 2

Views: 273

Answers (2)

Bergi
Bergi

Reputation: 664528

Calling window.requestAnimationFrame(anim); will never resolve your promise. It will call anim which will create another promise which might then get resolved if you're finished, but the original promise is never fulfilled.

Instead of wrapping the whole body of anim in a new Promise constructor, you should promisify only requestAnimationFrame and then use that to build a promise chain.

function animate(element){
    var el = element[index];
    var MaxHeight = false;
    var finished = false;
    function getFrame() {
        return new Promise(function(resolve) {
            window.requestAnimationFrame(resolve);
        });
    }
    function anim(){
        if (finished == false){
            if (MaxHeight == false){
                var height = parseInt(window.getComputedStyle(el).height.slice(0, -2));
                var Bot = parseFloat(window.getComputedStyle(el).bottom.slice(0, -2));
                height = height + 1;
                Bot = Bot + 0.5;
                el.style.bottom = Bot + "px";
                el.style.height = height + "px";
                if (height <= 100){
                    return getFrame().then(anim);
//                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                } else{
                    MaxHeight = true;
                }
            }
            if (MaxHeight == true){
                var height = parseInt(window.getComputedStyle(el).height.slice(0, -2));
                var Bot = parseFloat(window.getComputedStyle(el).bottom.slice(0, -2));
                height = height - 1;
                Bot = Bot - 0.5;
                el.style.bottom = Bot + "px";
                el.style.height = height + "px";
                if (height >= 50){
                    return getFrame().then(anim);
//                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                } else{
                    MaxHeight = true;
                    finished = true;
                    el.style.bottom = 0 + "px";
                    el.style.height = 50 + "px";
                }
            }
        }
        return Promise.resolve();
    }
    return anim().then(loading);
}

Upvotes: 0

Hin Fan Chan
Hin Fan Chan

Reputation: 1643

The problem is that not all path leads to resolve. The bigger problem is that your code creates a lot of promises instead of one. Here is the outline of the change.

function anim () {
  return new Promise (function (resolve) {
    function _anim () {
      if (!finished) {
          _logic();
          // By putting requestAnimationFrame at the end, you can ensure 
          // that it will be called after your logic
          // (assuming finished eventually equals true).
          window.requestAnimationFrame(_anim);
      }
     else 
       resolve();
    }
  });

Here is the actual fix.

var index = -1;

function loading() {
  var loader = document.getElementById("loader");
  display = window.getComputedStyle(loader).display;
  if (display == "block") {
    var child = document.getElementById("loader-ul").getElementsByTagName("div");
    index = index + 1;
    alert("dd");
    animate(child);
  }
}

function animate(element) {
  var el = element[index];
  var MaxHeight = false;
  var finished = false;

  function anim() {
    return new Promise(function(resolve, reject) {
      function _anim() {
        if (finished == false) {
          if (MaxHeight == false) {
            var height = parseInt(window.getComputedStyle(el).height.slice(0, -2));
            var Bot = parseFloat(window.getComputedStyle(el).bottom.slice(0, -2));
            height = height + 1;
            Bot = Bot + 0.5;
            el.style.bottom = Bot + "px";
            el.style.height = height + "px";
            if (height > 100) {
              MaxHeight = true;
            }
          }
          if (MaxHeight == true) {
            var height = parseInt(window.getComputedStyle(el).height.slice(0, -2));
            var Bot = parseFloat(window.getComputedStyle(el).bottom.slice(0, -2));
            height = height - 1;
            Bot = Bot - 0.5;
            el.style.bottom = Bot + "px";
            el.style.height = height + "px";
            if (height < 50) {
              MaxHeight = true;
              finished = true;
              el.style.bottom = 0 + "px";
              el.style.height = 50 + "px";
            }
          }
          window.requestAnimationFrame(_anim);
        } else {
          resolve();
        }
      }
      _anim();
    });
  }
  anim().then(loading);
}
body {
  margin: 0px;
  padding: 0px;
  margin: auto;
}
#loader {
  display: block;
  position: fixed;
  height: 100%;
  width: 100%;
  background-color: white;
  z-index: 9999;
}
#loader .center {
  position: relative;
  height: 50px;
  width: 200px;
  background-color: red;
  top: 50%;
  transform: translateY(-50%);
  margin: auto;
}
#loader .center div {
  width: 2px;
  height: 50px;
  background-color: blue;
  float: left;
  padding: 0px;
  margin-right: 5px;
  margin-bottom: 25px;
  position: relative;
}
<body onload="loading()">
  <div id="loader">
    <div class="center" id="loader-ul">
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
    </div>
  </div>
</body>

Upvotes: 1

Related Questions