【问题标题】:Why function is executed although await is used?为什么虽然使用了 await 却执行了函数?
【发布时间】:2021-12-28 13:14:15
【问题描述】:

我在主函数中使用了 await 关键字来等待对 poll() 的异步函数调用完成,但对 my_plot 的函数调用是在 poll() 函数完成之前进行的。

async function main() {
    getParametersData()
    await poll()
    my_plot()
} 
async function getData() {
    const response = await fetch(API)
    const message = await response.json()
    return message
}

async function poll(count = 1) {
    console.log(`Polling ${count}`);
    try {
        const data = await getData();
        if (data && Object.keys(data).length !== 0) {
            console.log("Poll", data)
            return;
        } else {
            setTimeout(poll, 5000, ++count);
        }
    } 
    catch (err) {
        console.log(`${err}. Polling again in 5 seconds.`);
        setTimeout(poll, 5000, 1);
    }

}

async function my_plot() {
    console.log("my plot")
}

代码输出:

Polling 1
my plot 
Polling 2
Polling 3
Poll [1,2,3]

预期:

Polling 1
Polling 2
Polling 3
Poll [1,2,3]
my plot

【问题讨论】:

  • 什么是getData()
  • @Dai 更新了代码
  • setTimeout 的运作方式与您想象的不同。它会导致您的 poll 方法的后续/递归调用异步执行,这就是您看到此行为的原因。这在associated MDN page 上有非常清楚的记录。
  • setTimeout 将启动一个新任务,您不能直接等待。您需要创建一个 Promise 并在超时回调中解决它,以便它的行为像您想要的那样。见How to make a promise from setTimeout
  • @esqew 还有其他方法可以完成我想做的事情吗?任何帮助或建议将不胜感激

标签: javascript async-await


【解决方案1】:

不要在async 函数中直接使用setTimeout。相反,请使用基于 Promise 的包装器。

令人惊讶的是,现代 ECMAScript 没有附带基于 Promise 的收件箱版本的 setTimeoutbut it's straightforward to implement

function delay( timeout ) {
    if( typeof timeout !== 'number' || timeout < 0 ) throw new Error( "Timeout must be a non-negative integer milliseconds delay value." );

   return new Promise( function( resolve ) { 
       setTimeout( resolve, timeout );
   });
}
  • 然后您可以使用“真实的”while 循环重写您的poll 函数,如下所示(如下)。
  • 我认为你的 poll 函数应该返回一个 true/false 值,以向调用者指示成功或失败,如果你需要的话。
  • 考虑使用typeof,而不是像Object.keys(data).length 这样安全性较低的检查 - 或至少使用Object.keys 使用typeof 检查之前
    • 虽然很烦人typeof null === 'object',所以你总是需要!== null 检查,抱怨...
    • 作为替代方案,考虑拥有自己的类型保护功能(是的,我知道这不是 TypeScript),这样您就可以更强有力地保证 data 包含您需要的内容(因为 JS 没有静态类型检查)。
async function poll( count = 1 ) {
    
    console.log(`Polling ${count}`);
 
    let i = 0;
    do {
        try {
            const data = await getData();
            if( isMyData( data ) ) {
                return true;
            }
        }
        catch( err ) {
            console.error( err );
        }

        console.log( "Polling again in 5 seconds." );
        await delay( 5000 );

        i++;
    }
    while( i < count );

    console.log( `Gave up after ${count} attempts.` );
    return false;
}

// Type-guard:
function isMyData( data ) {
    
    return (
        ( typeof data === 'object' )
        &&
        ( data !== null )
        &&
        ( 'this is my object' in data )
        &&
        ( data['there are many like it but this one is mine'] )
        &&
        ( data.myJavaScriptEngineIsMyBestFriend )
        &&
        data.itIsMyLife
        &&
        data.withoutMe_javaScriptIsUseless
        &&
        data.withoutJavaScript_iAmUseLess > 0
    );
}

请注意,如果您打算捕获 getData 引发的错误,则应使用最小范围的 try 而不是其中包含更多逻辑,因为通常您不想捕获不相关的错误。

【讨论】:

  • @SebastianSimon ? - 我改进了它
  • “令人惊讶的是,现代 ECMAScript 没有附带基于 Promise 的收件箱版本的 setTimeout — 参见 Bringing setTimeout to ECMAScript 和 @987654323 @.
  • @SebastianSimon 这些线程已有十多年的历史了,erk
【解决方案2】:

使用来自How to make a promise from setTimeout 的答案,您可以使用传统循环。

function later(delay, value) {
  return new Promise(resolve => setTimeout(resolve, delay, value));
}

async function poll() {
  for (let count = 1;; count++) {
    console.log(`Polling ${count}`);
    try {
      const data = Math.random(); // simulated data
      if (data < 0.2) { // simulated 20% success rate
        console.log("Poll", data)
        return data;
      } else {
        console.log("Retrying in 5 seconds");
        await later(5000);
      }
    } catch (err) {
      console.log(`${err}. Polling again in 5 seconds.`);
      count = 1;
      await later(5000);
    }
  }
}

async function main() {
  console.log("Start");
  await poll();
  console.log("Poll done");
}

main();

【讨论】:

    猜你喜欢
    • 2016-11-29
    • 1970-01-01
    • 2021-04-19
    • 2020-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-27
    • 2019-11-09
    相关资源
    最近更新 更多