使用异步函数+await+setTimeout的组合

693

我正在尝试使用新的异步特性,并希望解决我的问题能帮助未来的其他人。这是我目前可用的代码:

  async function asyncGenerator() {
    // other code
    while (goOn) {
      // other code
      var fileList = await listFiles(nextPageToken);
      var parents = await requestParents(fileList);
      // other code
    }
    // other code
  }

  function listFiles(token) {
    return gapi.client.drive.files.list({
      'maxResults': sizeResults,
      'pageToken': token,
      'q': query
    });
  }
问题在于我的 while 循环运行得太快,脚本向 Google API 发送了过多的请求。因此,我希望构建一个延迟请求的 sleep 函数。这样,我也可以使用此函数来延迟其他请求。如果有另一种延迟请求的方法,请告诉我。
无论如何,这是我的新代码,但它不起作用。请求的响应返回给 setTimeout 中的匿名异步函数,但我不知道如何将响应返回到 sleep 函数或初始的 asyncGenerator 函数。
  async function asyncGenerator() {
    // other code
    while (goOn) {
      // other code
      var fileList = await sleep(listFiles, nextPageToken);
      var parents = await requestParents(fileList);
      // other code
    }
    // other code
  }

  function listFiles(token) {
    return gapi.client.drive.files.list({
      'maxResults': sizeResults,
      'pageToken': token,
      'q': query
    });
  }

  async function sleep(fn, par) {
    return await setTimeout(async function() {
      await fn(par);
    }, 3000, fn, par);
  }

我已经尝试了一些选项:将响应存储在全局变量中,并从sleep函数返回它,匿名函数内的回调等。

19个回答

4

使用marked答案后,我遇到了一个lint错误[no-promise-executor-return],所以我在这里找到了已经修正的版本,使用花括号明确表达不返回任何内容的意图:

const timeout = (ms) =>
  new Promise(resolve => {
    setTimeout(resolve, ms)
  })

4
await setTimeout(()=>{}, 200);

如果你的Node版本是15或以上,它将可以正常工作。


1
即使在Node ^16中也无法工作。请参考https://dev59.com/RFwX5IYBdhLWcg3w2iou#57390854中的评论。 - Shailendra Shukla

2
我想指出一个强大的扩展功能,可以用于 Promise.all。一种相当优雅的解决方案是将一个 Promise 与超时(例如 new Promise((resolve) => setTimeout(resolve, timeout)))进行竞争,以使其仅在规定时间内有效。
await new Promise.race([myPromise, timeoutPromise])

一旦其中一个承诺完成,就会继续执行。 myPromise 然后可以在内部等待不同的超时时间,或者只是利用 Promise.all

const timeout = ms => new Promise((resolve) => setTimeout(resolve, ms));
await Promise.race([
    Promise.all([myPromise, timeout(500)]),
    timeout(5000)
]);

结果是一个异步调用,不会超过每秒两次的频率运行,如果出现某些(网络/服务器?)错误,则超时时间为5秒。

此外,您可以将此非常通用和可定制的函数设置为:

function callWithTimeout(promise, msTimeout=5000, throws=false) {
    const timeout = ms => new Promise((resolve, reject) =>
        setTimeout(throws ? reject : resolve, ms));
    await Promise.race([
        //depends whether you want to wait there or just pass the promise itself
        Promise.all([promise, timeout(500)]), 
        timeout(msTimeout)
    ]);
}
    

这最终让您可以自定义超时时间以及承诺在超时时应该成功还是抛出异常。拥有如此强大的通用实现可以为您节省很多麻烦。您还可以将字符串设置为throws,并将此变量绑定到reject以获取自定义错误消息:reject.bind(undefined, throws)

请注意,不应使用await传递承诺:

const myPromise = async x => x;
//will never time out and not because myPromise will finish immediatelly
callWithTimeout(await myPromise(), 2000, true); 
//will possibly timeout after 2 sec with an exception, or finish 
callWithTimeout(myPromise(), 2000, true); 

2
我基于Daveanswer设计了一个实用工具。
基本上,传入一个done回调函数,在操作完成时调用。
// Function to timeout if a request is taking too long
const setAsyncTimeout = (cb, timeout = 0) => new Promise((resolve, reject) => {
  cb(resolve);
  setTimeout(() => reject('Request is taking too long to response'), timeout);
});

这是我使用它的方式:
try {
  await setAsyncTimeout(async done => {
    const requestOne = await someService.post(configs);
    const requestTwo = await someService.get(configs);
    const requestThree = await someService.post(configs);
    done();
  }, 5000); // 5 seconds max for this set of operations
}
catch (err) {
  console.error('[Timeout] Unable to complete the operation.', err);
}

1
以下代码适用于Chrome和Firefox以及其他一些浏览器。
function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function sleep(fn, ...args) {
    await timeout(3000);
    return fn(...args);
}

但在Internet Explorer中,我得到了一个语法错误,针对"(resolve **=>** setTimeout..."


0

如何一次性记录所有响应?

async function sayHello(name) {
  let greet = `Hey! ${name} very nice to meet you bud.`;
  setTimeout(() => {
    return {
      greet,
      createdAt: new Date(),
    };
  }, 1000);
}

const response1 = async () => await sayHello("sounish");
const response2 = async () => await sayHello("alex");
const response3 = async () => await sayHello("bill");

async function getData() {
  const data1 = await sayHello("sounish");
  const data2 = await sayHello("alex");
  const data3 = await sayHello("bill");
  return { data1, data2, data3 };
}

Promise.all([sayHello("sounish"), sayHello("alex"), sayHello("bill")]).then(
  (allResponses) => {
    console.log({ allResponses });
  }
);

getData().then((allData) => {
  console.log({ allData });
});

0

我想解决一个我认为相关的问题:每30秒自动刷新页面的能力,但同时每秒更新屏幕上的一些计时器,并确保这两个功能不冲突。我的谷歌搜索引导我来到了你在这里提出的问题,所以我想分享一下我想出来的东西,以帮助其他人:

class Elapsed
{
    constructor()
    {
        this.start = performance.now()
    }
    elapsed_seconds( seconds )
    {
        let elapsed_seconds = ( performance.now() - this.start ) * 0.001
        return elapsed_seconds >= seconds
    }
}

async function sleep( seconds )
{
    let promise = new Promise(( resolve, _ ) => {
        setTimeout( () => resolve(), seconds * 1000 )
    })
    await promise
}

async function refresh()
{
    // do something
    return new Elapsed()
}

async function auto_refresh()
{
    while( true )
    {
        let last_refresh = await refresh()
        while( !last_refresh.elapsed_seconds( 30 ))
        {
            await update_counters()
            await sleep( 1 )
        }
    }
}

-1
setTimeout(async () => {
   var x = await asyncFunction(); 
}, 5000)

2
你的回答可能需要更多的支持性信息。请编辑以添加进一步的细节,例如引用或文档,以便其他人可以确认你的回答是否正确。你可以在帮助中心找到有关如何撰写良好答案的更多信息。 - moken

-7

这是一个更快的一行代码修复。

希望这能有所帮助。

// WAIT FOR 200 MILISECONDS TO GET DATA //
await setTimeout(()=>{}, 200);

2
不起作用。这个代码:await setTimeout(()=>{console.log('first')}, 200); console.log ('second') 打印出 second 然后是 first - gregn3
1
@gregn3 是的,这就是重点。这是一种非阻塞解决方案,在完成"阻塞操作"之外的主程序流程时,函数外部的代码仍然可以继续执行。虽然你、Rommy和Mohamad提供的语法不严谨,因为需要在异步函数中包含await (可能是最近才添加的),而我正在使用Node.js。这是我的调整后的解决方案。var test = async () => { await setTimeout(()=>{console.log('first')}, 1000); console.log ('second') }我扩展了超时时间以显示其有用性。 - azariah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接