Re: [问题] setTimeout与循环的问题

楼主: Kenqr (function(){})()   2016-11-24 16:33:52
※ 引述《shadowjohn (转角遇到爱)》之铭言:
: ※ 引述《iamshuichi (vincent)》之铭言:
: : audioPlay=[audio1.play(), audio2.play(), audio3.play();
: : time=[0, 500, 1000];
: : for (var i=0; i<3; i++) {
: : setTimeout("audioPlay[i]", time[i]);
: : }
: : 上面这段程式,我希望audio1播完之后
: : 等待500毫秒之后播audio2
: : 再等待500毫秒后播audio3
: : 但是失败了,我猜循环大概不能这样写吧!
: : 于是我修改了一下
: : for (var i=0; i<3; i++) {
: : setTimeout("audioPlay[i]", 500);
: : }
: : 结果还是不行,三个声音同时出来
: : 看来JavaScript是先等500毫秒
: : 再同时执行前面的三个函数
: : 如果想达到我的目的
: : 不知道该怎么写呢?
: for (var i=0; i<3; i++) {
: setTimeout("audioPlay[i]", 500);
: }
: 上面这样写是不行的
: 请改成下面这样
: for (var i=0;i<3; i++) {
: (function (index) { //加包的一层
: setTimeout(function () {
: audioPlay[index];
: }, 500);
: })(i); //带入循环的 i 值,会变成这个加包的function,变成 index 放入
: }
: 这样写就不用改太多了 :)
var audioPlay = [audio1.play, audio2.play, audio3.play];
var time = [0, 500, 1000];
for(var i=0; i<3; i++) {
setTimeout(audioPlay[i], time[i]);
}
实际可执行的范例:
https://jsfiddle.net/hzrw429z/1/
第一行的 audio1.play() 拿掉括号改成 audio1.play,
因为这个阵列里要放的是函数而不是函数的执行结果。
写成 audio1.play() 会在执行到宣告阵列这行时就播放了,函数也没存进阵列里。
setTimeout 这行,"audioPlay[i]" 拿掉双引号改成 audioPlay[i]。
因为 setTimeout 传入字串时,是把字串内容当成函数内容执行。
原本的写法会在时间到时取出 audioPlay 阵列第 i 项的值,
取出后没有做任何动作,所以什么事都没发生。
修正的写法会在呼叫 setTimeout 函数前先取得 audioPlay 的第 i 项,
取得的东西是一个函数,当成参数传给 setTimeout。
所以 3 次呼叫 setTimeout 相当于:
setTimeout(audio1.play, 0);
setTimeout(audio2.play, 500);
setTimeout(audio3.play, 1000);
时间到时可以正确的分别执行 3 个函数。
这个情况不需要使用 IIFE,
因为 audioPlay[i] 是在呼叫 setTimeout 之前就已经取值了。
setTimeout 在时间到时,呼叫第一个参数的函数时已经不会用到 i,
所以即使离开循环后 i 的值固定是 3,对我们也没有影响。
作者: iamshuichi (vincent)   2016-11-24 21:45:00
感谢大大的指导,这篇我比较看得懂,我研究看看!<audio id="audio1" src="1.wav"></audio><audio id="audio2" src="2.wav"></audio><audio id="audio3" src="3.wav"></audio>
作者: No (you stay there)   2016-11-25 00:29:00
setTimeout(audio1.play, 0) 这种写法是 anti-pattern除非你很清楚自己在做什么,不然不要这样写

Links booklink

Contact Us: admin [ a t ] ucptt.com