如何让setTimeout中的函数完整执行?

How can I make the function in setTimeout execute completely?

初学js,想输出5个'!',间隔1秒,最后输出"end"。 我认为问题与异步有关。 我尝试了很多次,尝试了很多方法,比如“await,async”和“promise”,但还是失败了。

class A {
  constructor() {
    this.cnt = 5;
  }

  real() {
    this.cnt--;
    console.log("!");
  }

  getCnt() {
    return this.cnt;
  }
}

class B {
  constructor() {
    this.a = new A();
  }
  fun() {
    if (this.a.getCnt() > 0) {
      this.a.real();
      setTimeout(() => this.fun(), 1000);
    }
  }
}

class C {
  constructor() {
    this.b = new B();
  }
  f() {
    this.b.fun();
    console.log("end");
  }
}
var c = new C();
c.f();

跳过涉及的 3 个 类 的复杂性,这可以用 async 函数优雅地解决。 (没有异步函数,循环中 setTimeout 的级联变得更难管理。)

如果需要,这当然可以包装成三个 类。

// Turns `setTimeout` into a promise you can `await`.
async function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

// Calls a function `fun` `times` times, delaying for `delayTime` ms between each invocation.
// Since this is an async function, it returns a promise that will resolve as soon as it's done,
// which in turn can be awaited upon.
async function repeatWithDelay(fun, times, delayTime) {
  for (let i = 0; i < times; i++) {
    await fun(i);
    await delay(delayTime);
  }
}

// Prints the number passed in (could just print an exclamation mark).
function print(i) {
  console.log(`${i}!`);
}

async function main() {
  console.log("Go!");
  await repeatWithDelay(print, 5, 500);
  console.log("Done!");
}

// Assumes top-level `await` is not available in your environment.
// If it is, this too can be replaced with a simple `await`.
main().then(() => {
  console.log("Main done.");
});

这会打印出来

Go!
0!
1!
2!
3!
4!
Done!
Main done.

最后,您需要让您的方法 fun 能够在完成时报告,因为它是一个异步方法(调用 setTimeout)。最好的方法是 return a Promise 允许调用在其上使用 await

fun() {
    return new Promise(resolve => {    
      
      const exec = () => {
        if (this.a.getCnt() > 0) {
          this.a.real();
          setTimeout(exec.bind(this), 1000);
        }
        else{
          resolve();
        }
      }
      
      exec.apply(this);
    })
  }

完成后,还将函数 f 标记为 async,这样您就可以在其中调用 await

async f() {
  await this.b.fun();
  console.log("end");
}

然后一切按预期工作:

class A {
  constructor() {
    this.cnt = 5;
  }

  real() {
    this.cnt--;
    console.log("!");
  }

  getCnt() {
    return this.cnt;
  }
}

class B {
  constructor() {
    this.a = new A();
  }
  fun() {
    return new Promise(resolve => {    
      
      const exec = () => {
        if (this.a.getCnt() > 0) {
          this.a.real();
          setTimeout(exec.bind(this), 1000);
        }
        else{
          resolve();
        }
      }
      
      exec.apply(this);
    })
  }
}

class C {
  constructor() {
    this.b = new B();
  }
  async f() {
    await this.b.fun();
    console.log("end");
  }
}

var c = new C();
c.f();

我尝试编写一个模拟输入的通用函数,希望这能帮助您理解 async/await 函数

let fakeConsole = document.getElementById('console');

async function sleep(time) {
  await new Promise(r => setTimeout(r, time));
}

function type(text) {
  fakeConsole.innerHTML += text;
}

async function emulateTyping(text, speed) {
  // split the text into an array of characters;
  const characters = text.split('').reverse();
  if (characters.length > 0) {
    // display first character right away
    type(characters.pop());
    while (characters.length > 0) {
      // wait <speed> millisections
      await sleep(speed);
      // type one character
      type(characters.pop());
    }
  }
}

async function main() {
  // async function we wait for the end with "await"
  await emulateTyping("!!!!!", 500);
  // sync function
  type(' END');
};

main();
<pre id="console"></pre>

如果使用 promise,那么你也需要解析 setTimeout 中的响应

class A {
  constructor() {
    this.cnt = 5;
  }

  real() {
    this.cnt--;
    console.log("!");
  }

  getCnt() {
    return this.cnt;
  }
}

class B {
  constructor() {
    this.a = new A();
  }
  fun() {
    return new Promise((resolve) => {
      if (this.a.getCnt() > 0) {
        this.a.real();
        setTimeout(() => resolve(this.fun()), 1000);
      } else {
        resolve()
      }
    })

  }
}

class C {
  constructor() {
    this.b = new B();
  }
  f() {
    this.b.fun().then(() => {
      console.log("end");
    })

  }
}
var c = new C();
c.f();

对于多个循环:

var loop = [array of 100 items];
await Promise.all(
   loop.map(async (item) => await this.b.fun();)
)

console.log("end");