非同期機能+ await + setTimeoutの組み合わせ


306

私は新しい非同期機能を使用しようとしています。私の問題を解決することが将来的に他の人の役に立つことを願っています。これは機能している私のコードです:

  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ループの実行速度が速すぎ、スクリプトが1秒あたりの送信リクエスト数が多すぎることです。そのため、リクエストを遅延させるスリープ機能を構築したいと思います。したがって、この関数を使用して他のリクエストを遅らせることもできます。リクエストを遅らせる別の方法がある場合は、お知らせください。

とにかく、これは動作しない私の新しいコードです。リクエストの応答は、setTimeout内の匿名非同期関数に返されますが、スリープ関数respに応答を返す方法がわかりません。最初の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);
  }

私はすでにいくつかのオプションを試しました:グローバル変数に応答を保存して、それをスリープ関数から返す、無名関数内のコールバックなど。

回答:


615

あなたのsleepための機能は動作しませんsetTimeout(まだ?)かもしれない約束戻らないawait編。手動でそれを約束する必要があります:

function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function sleep(fn, ...args) {
    await timeout(3000);
    return fn(...args);
}

ところで、ループを遅くするためにsleep、コールバックを受け取り、それをこのように延期する関数を使用したくないでしょう。私はむしろ次のようなことをすることをお勧めします

while (goOn) {
  // other code
  var [parents] = await Promise.all([
      listFiles(nextPageToken).then(requestParents),
      timeout(5000)
  ]);
  // other code
}

の計算にparentsは少なくとも5秒かかります。


11
Promise.allアプローチが大好きです。とてもシンプルでエレガント!
Anshul Koka 2017

4
の表記は何をvar [parents]表していますか?私はこれまでに見たことがなく、グーグルするのは難しいことです
natedog 2017

6
@NateUsher 配列の
ベルギ

1
@tinkerr " タイムアウトを待つ必要がある場合は、非同期で宣言する必要があります " -いいえ。関数は、待機できるプロミスを返すだけで十分です(実際には、thenableで十分です)。それを実現する方法は、関数の実装次第ですが、である必要はありませんasync function
ベルギ

2
@naisanzaいいえ、async/ awaitは約束に基づいています。置き換えるのはthen呼び出しだけです。
Bergi

152

ノード7.6以降promisify、utilsモジュールの関数functionをと組み合わせることができますsetTimeout()

Node.js

const sleep = require('util').promisify(setTimeout)

JavaScript

const sleep = m => new Promise(r => setTimeout(r, m))

使用法

(async () => {
    console.time("Slept for")
    await sleep(3000)
    console.timeEnd("Slept for")
})()

1
nodeJSでawait require('util').promisify(setTimeout)(3000)もによって必要とすることなく達成することができる:await setTimeout[Object.getOwnPropertySymbols(setTimeout)[0]](3000)
SHL

5
興味深い@Shl。私のソリューションよりも読みにくいと思います。人々が同意しない場合、それをソリューションに追加できますか?
ハリー

2
requireバージョンは明らかにgetOwnPropertySymbolsバージョンよりはるかに優れています...それが壊れていなければ...!
Matt Fletcher、

2
@Harryさん、こんにちは。FlavorScapeの回答の1つのライナーを自分の回答に組み込んだようです。私はあなたの意図を推測したくありませんが、それは彼らにとって本当に公平ではありません。編集をロールバックできますか?今のところ、それは盗作のように見えます。
FélixGagnon-Grenier

2
回答はすぐ下にあるため、ワンライナーを削除しましたが、ほとんどの読者は最初のいくつかの回答を見逃すことはないため、多くの人気のある回答が回答を更新して他の新しい回答を含めるのを見てきました。
ハリー

130

迅速なワンライナー、インライン方式

 await new Promise(resolve => setTimeout(resolve, 1000));

4
let sleep = ms => new Promise( r => setTimeout(r, ms));//ワンライナー関数
Soldeplata Saketos '28 / 08/28

8
さらに短い:-)await new Promise(resolve => setTimeout(resolve, 5000))
Liran Brimer

1
同じ行で "resolve" x 2回使用するとはどういう意味ですか?のように:await new Promise(resolve => setTimeout(resolve、1000)); それは参照しますか それ自体または何に?代わりに次のようなことをします。function myFunc(){}; 新しいPromise(resolve => setTimeout(myFunc、1000));を待ちます。
PabloDK

35

setTimeoutasync関数ではないため、ES7 async-awaitでは使用できません。ただし、sleepES6 Promiseを使用して関数を実装できます。

function sleep (fn, par) {
  return new Promise((resolve) => {
    // wait 3s before calling fn(par)
    setTimeout(() => resolve(fn(par)), 3000)
  })
}

次に、この新しいsleep関数をES7 async-awaitで使用できるようになります。

var fileList = await sleep(listFiles, nextPageToken)

、してくださいは、ノート私だけでES7非同期/のawaitを組み合わせることについての質問に答えるよというsetTimeoutことは毎秒あまりにも多くのリクエストを送信すると、あなたの問題を解決するためではないかもしれませんが、。


更新:最新のnode.jsバージョンには、組み込みの非同期タイムアウト実装があり、util.promisifyヘルパーからアクセスできます。

const {promisify} = require('util');
const setTimeoutAsync = promisify(setTimeout);

2
fnスローがキャッチされない場合、それを行うべきではありません。
Bergi、2015年

@Bergi new Promiseできるところまで泡だと思いますsleep.catch
Florian Wendelborn 2017年

3
@Dodekeractいいえ、非同期setTimeoutコールバックにあり、new Promiseコールバックは長い間行われてきました。グローバルコンテキストにバブルし、未処理の例外としてスローされます。
Bergi

>毎秒送信するリクエストが多すぎる問題。「デバウンス」を使用して、UIがあまりにも多くのruquestを起動することなどを防ぐ必要があります。
FlavorScape

5

次のようなsetTimeoutヘルパー関数を記述できるのと同じ種類の構文を使用したい場合:

const setAsyncTimeout = (cb, timeout = 0) => new Promise(resolve => {
    setTimeout(() => {
        cb();
        resolve();
    }, timeout);
});

その後、次のように呼び出すことができます。

const doStuffAsync = async () => {
    await setAsyncTimeout(() => {
        // Do stuff
    }, 1000);

    await setAsyncTimeout(() => {
        // Do more stuff
    }, 500);

    await setAsyncTimeout(() => {
        // Do even more stuff
    }, 2000);
};

doStuffAsync();

私は要旨を作りました:https : //gist.github.com/DaveBitter/f44889a2a52ad16b6a5129c39444bb57


1
のような関数名delayRunは、コールバック関数の実行をX秒遅らせるため、ここではより意味があります。非常に待ち遠しい例ではありません、IMO。
mix3d

2
var testAwait = function () {
    var promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('Inside test await');
        }, 1000);
    });
    return promise;
}

var asyncFunction = async function() {
    await testAwait().then((data) => {
        console.log(data);
    })
    return 'hello asyncFunction';
}

asyncFunction().then((data) => {
    console.log(data);
});

//Inside test await
//hello asyncFunction

0

次のコードは、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

Dave回答に触発されたユーティリティを作成しました

基本的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);
}

0

これは、AWS Labsで2020年にnodejsを使用した私のバージョンです

const sleep = require('util').promisify(setTimeout)

async function f1 (some){
...
}

async function f2 (thing){
...
}

module.exports.someFunction = async event => {
    ...
    await f1(some)
    await sleep(5000)
    await f2(thing)
    ...
}

-3

これは、ワンライナーでのより迅速な修正です。

これがお役に立てば幸いです。

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

1
動作しません。これawait setTimeout(()=>{console.log('first')}, 200); console.log ('second')は、2番目最初に
gregn3

1
はい、ポイントです@ gregn3。これは非ブロッキングソリューションであり、メインプログラムフローの外部で「ブロッキング操作」が完了している間、関数の外のコードが実行を継続できます。あなたとRommyとMohamadが提供した構文は、非同期関数でラップされるのを待つ必要があるため、厳密には正しくありませんが(かなり最近追加された可能性があります)、node.jsも使用しています。これは私の微調整したソリューションです。var test = async () => { await setTimeout(()=>{console.log('first')}, 1000); console.log ('second') }タイムアウトの有用性を示すためにタイムアウトを延長しました。
azariah
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.