为什么 await sleep 提前退出 for 循环?

Why does an await sleep exits a for loop early?

我尝试在单击 next/previous 按钮时为音乐应用程序制作淡入淡出效果; 我有这个睡眠功能:

const sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));

按下按钮时的功能:

function nextSong() {
  songIndex++;
  if (songIndex > songs.length - 1) {
    songIndex = 0;
  }
  fadeOut(audio);
  loadSong(songs[songIndex]);
  playSong();
  fadeIn(audio);
}

和衰减函数

async function fadeOut(soundtrack){

    for (var i = 1.0; i > 0; i-= 0.01)
    {
        console.log(i);
        console.log(soundtrack.volume);
        soundtrack.volume = i;
        await sleep(2000);
    }
}

async function fadeIn(soundtrack){

    for (var i = 0; i <= 1; i+= 0.01)
    {
        console.log(i);
        console.log(soundtrack.volume);
        soundtrack.volume = i;
        await sleep(100);
    }
}

问题是 fadeOut 根本不起作用,它进入 for 循环进行 1 次迭代然后存在。 同时,淡入效果完美。我就是不明白。顺便说一句,这是我的第一个 javascript 爱好项目。

函数 returns 到达 await 是正常的。它 return 是一个承诺。您必须确保调用者还等待 that 承诺的解决,它只会在第一个函数完成其所有任务时解决。

所以:

async function nextSong() {
//^^^^
  songIndex = (songIndex + 1) % songs.length;
  await fadeOut(audio);
//^^^^^
  await loadSong(songs[songIndex]);
  await playSong();
  await fadeIn(audio);
}

我在这里假设 loadSongplaySong 也 return 承诺,因此这些调用也需要 await 运算符。

看起来问题可能出在您的 nextSong 函数上。您正在调用多个 async 函数,每个函数 运行 彼此独立。您希望 fadeOut 在 loading/playing 下一首歌曲之前完成。

有两种解决方法。第一个是使用 .then 在 淡出当前歌曲后 加载下一首歌曲:

function nextSong() {
  songIndex++;
  if (songIndex > songs.length - 1) {
    songIndex = 0;
  }

  fadeOut(audio).then(() => {
    loadSong(songs[songIndex]);
    playSong();
    fadeIn(audio);
  });
}

或者,您可以使函数 asyncawait 调用 fadeOut 函数:

async function nextSong() {
  songIndex++;
  if (songIndex > songs.length - 1) {
    songIndex = 0;
  }

  await fadeOut(audio);
  loadSong(songs[songIndex]);
  playSong();
  fadeIn(audio);
}

同意前面的回答。我还认为,鉴于这是一个项目,您可以在不使用 async/await.

的情况下创建具有这些功能的音乐播放器

我在 HTML 中使用 archive.org mp3 url 作为音频标签创建了一个片段,并且能够让它们在跳过歌曲时淡入淡出。

另外一个想法,既有函数声明又有表达式。了解其中的区别很重要。函数声明在执行任何代码之前加载,而函数表达式仅在解释器到达该行代码时加载。

函数表达式

alert(foo()); // ERROR! foo wasn't loaded yet
var foo = function() { return 5; }

函数声明

alert(foo()); // Alerts 5. Declarations are loaded before any code can run.
function foo() { return 5; }

当您想在全局范围内创建函数并使其在整个代码中可用时,请使用函数声明。使用函数表达式限制函数可用的位置,保持全局范围简洁,并保持语法简洁。

除非需要函数声明,否则我更喜欢在整个代码中使用函数表达式。

如果您想练习使用 async/await,您可以查看这些其他 Whosebug 问题。

How and when to use ‘async’ and ‘await’

What is the use case for async/await?