Typescript - 来自for循环中动态变量的setTimeout自定义时间
Typescript - setTimeout custom time from variable that is dynamic in for loop
所以我有这个函数,它的超时每 3 秒改变一次:
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
}
}
现在我想将时间 (3000) 更改为我从 促销 获得的自定义变量。这是促销活动:
所以每张图片或 i 的实例都有自己的时间。
这是我所做的:
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));
}
这在理论上应该可行,但计时器走得太远了。
第一张照片应该持续4s,但持续3s。第二张图片应该持续 3 秒,但持续 6 秒。第三张图应该是10秒却持续了4秒...
我不明白我做错了什么。为什么即使我发送了正确的变量 promoDisplayTime.
计时器也会关闭
具有虚拟数据的 StackBlitz:https://stackblitz.com/edit/angular-r6ejw9?file=src%2Fapp%2Fapp.component.html
在 Angular 上下文中工作时,我建议拥抱 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")
);
}
然后在您的模板中:
<img [src]="promotions$ | async" alt="Responsive image of item">
现场演示:https://stackblitz.com/edit/angular-xjcrgd?file=src/app/app.component.ts
编辑:我不确定你的问题中的时间是否是相对的,但在再次阅读之后我认为是。如果是这种情况,那么您可以构建一个重映射函数来获得这样的绝对时间:
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;
}, []);
然后您将照片排序为 1、2、3。
现场演示:https://stackblitz.com/edit/angular-6c4pqt?file=src/app/app.component.ts
我看到两个错误:
您从 0 开始计数 i
。结果是当您计算超时值时第一个值(6 秒)变为 0,因此几乎立即执行第一个函数。然后第二个值(3 秒)用于显示第二张图片,并乘以 i
,此时为 1(因此您看到第一个持续 3 秒)。第三张图同理
您使用 i
作为某种分隔符同时调用所有 setTimeout
。要实现您的需要,您应该在 setTimeout
for picture n
结束后调用 setTimeout
for picture n + 1
。
根据这个答案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.
所以发生的事情是 for 循环以 promotions[i].time = 4
结束,并且 setTimeout
代码段以该值运行(至少这是我在 StackBlitz 中的代码中看到的)...
这是使用异步函数和 setTimeout() 的可能解决方案之一。
使用异步功能,我们确保下一张幻灯片不会在第一张幻灯片之前执行。
你应该注意 setTimeout 的事情是定时器将在时间到期后执行给定的功能。
所以工作示例应该是这样的:
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);
}
}
}
所以我有这个函数,它的超时每 3 秒改变一次:
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
}
}
现在我想将时间 (3000) 更改为我从 促销 获得的自定义变量。这是促销活动:
所以每张图片或 i 的实例都有自己的时间。
这是我所做的:
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));
}
这在理论上应该可行,但计时器走得太远了。 第一张照片应该持续4s,但持续3s。第二张图片应该持续 3 秒,但持续 6 秒。第三张图应该是10秒却持续了4秒...
我不明白我做错了什么。为什么即使我发送了正确的变量 promoDisplayTime.
计时器也会关闭具有虚拟数据的 StackBlitz:https://stackblitz.com/edit/angular-r6ejw9?file=src%2Fapp%2Fapp.component.html
在 Angular 上下文中工作时,我建议拥抱 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")
);
}
然后在您的模板中:
<img [src]="promotions$ | async" alt="Responsive image of item">
现场演示:https://stackblitz.com/edit/angular-xjcrgd?file=src/app/app.component.ts
编辑:我不确定你的问题中的时间是否是相对的,但在再次阅读之后我认为是。如果是这种情况,那么您可以构建一个重映射函数来获得这样的绝对时间:
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;
}, []);
然后您将照片排序为 1、2、3。
现场演示:https://stackblitz.com/edit/angular-6c4pqt?file=src/app/app.component.ts
我看到两个错误:
您从 0 开始计数
i
。结果是当您计算超时值时第一个值(6 秒)变为 0,因此几乎立即执行第一个函数。然后第二个值(3 秒)用于显示第二张图片,并乘以i
,此时为 1(因此您看到第一个持续 3 秒)。第三张图同理您使用
i
作为某种分隔符同时调用所有setTimeout
。要实现您的需要,您应该在setTimeout
for picturen
结束后调用setTimeout
for picturen + 1
。
根据这个答案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.
所以发生的事情是 for 循环以 promotions[i].time = 4
结束,并且 setTimeout
代码段以该值运行(至少这是我在 StackBlitz 中的代码中看到的)...
这是使用异步函数和 setTimeout() 的可能解决方案之一。
使用异步功能,我们确保下一张幻灯片不会在第一张幻灯片之前执行。
你应该注意 setTimeout 的事情是定时器将在时间到期后执行给定的功能。
所以工作示例应该是这样的:
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);
}
}
}