0

So I have this function that has a timeout that changes every 3s:

 setActiveImage(promotions) {
    for (let i = 0; i <= promotions.length - 1; i++) {
      setTimeout(()=> {
        this.activeImage = 'http://myrul/public/Commercials/' + promotions[i].commercial_file[0].file;
      }, 3000*(i)); //CHANGE PICTURE EVERY 3s

    }
  }

Now I want change the time (3000) to a custom variable that I get from promotions. This is promotions:

enter image description here

So every picture, or instance of i, has it's own time.

Here is what I did:

for (let i = 0; i <= promotions.length - 1; i++) {

  var x = promotions[i].time; //GET TIME
  var y = +x; //TURN STRING TO NUMBER
  var z = y * 1000; //TURN SECOND INTO MILISECONDS

  var promoDisplayTime = z; //CUSTOM TIME

  setTimeout(()=> {
    this.activeImage = 'http://myurl/Commercials/' + promotions[i].commercial_file[0].file;
  }, promoDisplayTime*(i));

}

This in theory should work, but the timer goes way off. The first picture should last 4s, but lasts 3s. The second picture should last 3s but lasts 6s. Third picture should be 10s but lasts 4s...

I don't see what I am doing wrong. Why is the timer off even though I am sending the correct variable promoDisplayTime.


StackBlitz with dummy data: https://stackblitz.com/edit/angular-r6ejw9?file=src%2Fapp%2Fapp.component.html

3
  • You're not using correctly setTimeout in loop. Please, see stackoverflow.com/questions/5226285/… Commented Jan 27, 2020 at 12:03
  • Don't increment it by i. You only want the time from the promotions - promoDisplayTime. Commented Jan 27, 2020 at 12:09
  • His problem is not that, it's rather, that PromoDisplayTime * i is absolutely not what he wants. Lets say the first ten images should take one second, and the eleventh should take ten seconds. For the eleventh image, PromoDisplayTime * i will be 10000 * 10 (loop starts at 0), so almost two minutes. He has to sum all the values. Commented Jan 27, 2020 at 12:09

4 Answers 4

1

Here is one of the possible solutions with the async function and setTimeout().

With async function, we make sure that the next slide is not executed before the first one is.

The thing that you should watch out with the setTimeout is that the timer will execute the given function AFTER the time expires.

So the working example should be something like this:

activeImage: string = 'https://pngimage.net/wp-content/uploads/2018/06/start-png-.png';

  promotions = [
    {
      time: 6,
      pic: 'https://pngimage.net/wp-content/uploads/2018/06/1-an-png-1.png'
    },
    {
      time: 3,
      pic: 'https://upload.wikimedia.org/wikipedia/commons/1/10/MRT_Singapore_Destination_2.png'
    },
    {
      time: 4,
      pic: 'https://upload.wikimedia.org/wikipedia/commons/a/aa/L%C3%ADnea_3_CAMETRO.png'
    }
  ];

  ngOnInit() {
      this.setActiveImage();
  }

  setActiveImage() {
    let _this = this;
    countDown();

    function printer(i) {

      return new Promise(resolve => {
        _this.activeImage = _this.promotions[i].pic;
        setTimeout(function () {
          resolve(true);
        }, _this.promotions[i].time * 1000);
      });
    }

    async function countDown() {
      for (var i = _this.promotions.length -1; i >= 0; i--) {
        await printer(i);
      }
    }

  }

Live demo: https://stackblitz.com/edit/angular-qwtgm2

Sign up to request clarification or add additional context in comments.

Comments

0

I see two mistakes:

  1. You count i from 0. The result is that the first value (6 seconds) becomes 0 when you calculate timeout value so first function is executed almost immediately. Then the second value (3 seconds) is used for showing the second picture and is multiple by i which is 1 at that point (so you see the first one lasting 3 seconds). The third picture follows the same logic.

  2. You call all setTimeouts at the same time using i as some sort of separator. To achieve what you need you should call setTimeout for picture n + 1 after the setTimeout for picture n ends.

Comments

0

per this answer Is setTimeout a good solution to do async functions with javascript?

setTimeout(function(){...}, 0) simply queues the code to run once the current call stack is finished executing.

so what's happens is the for loop finishes with promotions[i].time = 4 and the setTimeout piece of code runs with that value (at least this is what I see in your code in StackBlitz)...

Comments

0

When working within an Angular context, I'd recommend to embrace to power of RxJs.

const MOCKED_DATA = [
  {
    time: "6",
    pic: "https://pngimage.net/wp-content/uploads/2018/06/1-an-png-1.png"
  },
  {
    time: "3",
    pic:
      "https://upload.wikimedia.org/wikipedia/commons/1/10/MRT_Singapore_Destination_2.png"
  },
  {
    time: "4",
    pic:
      "https://upload.wikimedia.org/wikipedia/commons/a/aa/L%C3%ADnea_3_CAMETRO.png"
  }
];

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  promotions$ = from(MOCKED_DATA).pipe(
    map(promotion => of(promotion.pic).pipe(delay(1000 * +promotion.time))),
    mergeAll(),
    startWith("https://pngimage.net/wp-content/uploads/2018/06/start-png-.png")
  );
}

and then in your template:

<img [src]="promotions$ | async" alt="Responsive image of item">

Live demo: https://stackblitz.com/edit/angular-xjcrgd?file=src/app/app.component.ts

Edit: I'm not sure whether the time is relative or not in your question but after reading it again I think it is. If that's the case, then you could build a remap function to have an absolute time like that:

const remapToAbsoluteTime = (data: ServerContent[]): LocalContent[] =>
  data.reduce((acc, x) => {
    if (!acc.length) {
      acc.push({
        ...x,
        time: +x.time
      });
    } else {
      acc.push({
        ...x,
        time: acc[acc.length - 1].time + +x.time
      });
    }

    return acc;
  }, []);

Then you'd have the photos ordered 1, 2, 3.

Live demo: https://stackblitz.com/edit/angular-6c4pqt?file=src/app/app.component.ts

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.