1. 错误示例
1 2 3 4 5 6 7
   | function print() {   for (var i = 0; i < 10; i++) {       setTimeout(() => {           console.log(i);       }, 1000);   }  }
  | 
 
上面的代码,我们期望每隔一秒,输出一个数,而且这个数是每次循环i的值。但是期望也只是期望,实际情况是一秒钟之后直接输出了10个10。那么问题就来了,我们怎么实现我们的期望值呢?
由此我们先来尝试几种解决方案,并作一一对比:
- 每隔一秒输出一个10
1 2 3 4 5 6 7 8
   |  function print () {     for (var i = 0; i < 10; i++) {         setTimeout(() => {             console.log(i);         }, i * 1000);     } }
 
  | 
 
- 等待一秒之后接连输出0-9
1 2 3 4 5 6 7 8 9 10
   |  function print () {     for (var i = 0; i < 10; i++) {         (function (i) {             setTimeout(() => {                 console.log(i);             }, 1000);         })(i);     } }
 
  | 
 
 
综上所述,第一种方式实现了每隔一秒输出一个数值的功能,第二种方案实现了每次输出不同的i值的功能,如果将这两种方案综合起来,那就是我们想要的结果,即:
1 2 3 4 5 6 7 8 9 10
   |  function print () {     for (var i = 0; i < 10; i++) {         (function (i) {             setTimeout(() => {                 console.log(i);             }, i * 1000);         })(i);     } }
 
  | 
 
2. 问题及解决办法
之所以会出现这种情况差异,是因为 js 是单线程的,有一个事件队列机制,setTimeout 和 setInterval 的回调会到了延迟时间塞入事件队列中,排队执行。而在此同时,for循环并不会一直等待,而会直接循环完毕,所以等到 setTimeout 开始执行的时候,原先我们预想的i早就变成了10,所以就会出现之前我们遇到的问题。
为了实现我们想要的功能,就要解决两个问题:
- 怎么保存每次循环的临时变量 i
 
 
- 怎么让定时器产生时间间隔
 
 
弄懂了我们要解决的问题,那么接下来敲代码就行云流水了,关于第一个问题,我们需要保存每次循环 i 的值,关于第二个问题,我们只需要控制定时器的时间就好,以下即是几种解决方案:
3. 正确示例
1. 添加一个函数调用
1 2 3 4 5 6 7 8 9 10 11 12
   | function print () {     for (var i = 0; i < 10; i++) {         timePrint(i);         } }
 
  function timePrint (i) {     setTimeout(() => {         console.log(i);     }, i * 1000); }
  | 
 
2. 立即执行函数(闭包)
1 2 3 4 5 6 7 8 9
   |  function print () {     for (var i = 0; i < 10; i++) {         (function (j) {             setTimeout(() => {                 console.log(j);             }, i * 1000);         })(i);     } }
  | 
 
3. setTimeout另一种用法
1 2 3 4 5 6 7
   | function print() {   for (var i = 0; i < 10; i++) {       setTimeout(function(j) {         console.log(j);       }, 1000 * i, i);         }  }
  | 
 
setTimeout()的返回值是一个正整数,表示定时器的编号。这个值可以传递给clearTimeout()来取消该定时器。需要注意的是setTimeout()和setInterval()共用一个编号池,技术上,clearTimeout()和 clearInterval() 可以互换。但是,为了避免混淆,不要混用取消定时函数。
4. let方法保存不同阶段的值
1 2 3 4 5 6 7
   | function print() {   for (let i = 0; i < 10; i++) {       setTimeout(() => {           console.log(i);       }, i * 1000);   }  }
  | 
 
5. Promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | const tasks = [];
  for (var i = 0; i < 10; i++) {       (function(j) {         tasks.push(new Promise(resolve => {             setTimeout(() => {                 console.log(j);                 resolve();             }, 1000 * j);         }))       })(i);   } 
  Promise.all(tasks);
   | 
 
6. 简洁版的Promise
1 2 3 4 5 6 7 8 9 10 11 12 13
   | const tasks = []; const output = (i) => new Promise(resolve => {     setTimeout(() => {         console.log(i);         resolve();     }, 1000 * i); })
  for (var i = 0; i < 10; i++) {     tasks.push(output(i)); } 
  Promise.all(tasks);
   | 
 
7. sleep方法
1 2 3 4 5 6 7 8 9 10 11 12 13
   |  const sleep = (time) => new Promise(resolve => {     setTimeout(resolve, time); })
  async function print () {     for (var i = 0; i < 10; i++) {         await sleep(1000);         console.log(i);     }  };
  print();
 
  | 
 
8. bind方法
1 2 3 4 5 6 7
   | function print() {   for (var i = 0; i < 10; i++) {       setTimeout(function(i) {           console.log(new Date, i);       }.bind(null,i), 1000 * i);   } }
  |