Kombinasi fungsi async + menunggu + setTimeout

307

Saya mencoba menggunakan fitur async baru dan saya berharap menyelesaikan masalah saya akan membantu orang lain di masa depan. Ini adalah kode saya yang berfungsi:

  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
    });
  }

Masalahnya adalah, bahwa loop sementara saya berjalan terlalu cepat dan script mengirim terlalu banyak permintaan per detik ke Google API. Oleh karena itu saya ingin membangun fungsi tidur yang menunda permintaan. Jadi saya juga bisa menggunakan fungsi ini untuk menunda permintaan lainnya. Jika ada cara lain untuk menunda permintaan, harap beri tahu saya.

Bagaimanapun, ini adalah kode baru saya yang tidak berfungsi. Respons permintaan dikembalikan ke fungsi async anonim dalam setTimeout, tetapi saya tidak tahu bagaimana saya bisa mengembalikan respons ke resp fungsi tidur. ke fungsi asyncGenerator awal.

  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);
  }

Saya sudah mencoba beberapa opsi: menyimpan respons dalam variabel global dan mengembalikannya dari fungsi tidur, panggilan balik dalam fungsi anonim, dll.

JShinigami
sumber

Jawaban:

615

Anda sleepfungsi tidak bekerja karena setTimeouttidak (belum?) Kembali janji yang bisa awaited. Anda perlu mengesahkannya secara manual:

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

Btw, untuk memperlambat lingkaran Anda, Anda mungkin tidak ingin menggunakan sleepfungsi yang menerima panggilan balik dan menolaknya seperti ini. Saya lebih suka merekomendasikan untuk melakukan sesuatu seperti

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

yang memungkinkan perhitungan parentswaktu setidaknya 5 detik.

Bergi
sumber
11
Sukai Promise.allpendekatannya. Sangat sederhana dan elegan!
Anshul Koka
4
apa yang var [parents]diwakili oleh notasi ? Saya belum pernah melihatnya sebelumnya dan ini adalah hal yang sulit untuk google
natedog
6
@NateUsher Penghancuran susunan array
Bergi
1
@tinkerr " batas waktu harus dinyatakan async jika perlu ditunggu " - Tidak. Suatu fungsi hanya perlu mengembalikan janji yang dapat ditunggu (atau sebenarnya, yang cukup sudah cukup). Bagaimana mencapai itu hingga pelaksanaan fungsi, itu tidak perlu menjadi async function.
Bergi
2
@naisanza ada, async/ awaityang didasarkan pada janji-janji. Satu-satunya yang diganti adalah thenpanggilan.
Bergi
152

Sejak Node 7.6 , Anda dapat menggabungkan fungsi promisifyfungsi dari modul utils dengan setTimeout().

Node.js

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

Javascript

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

Pemakaian

(async () => {
    console.time("Slept for")
    await sleep(3000)
    console.timeEnd("Slept for")
})()
Harry
sumber
1
Dalam nodeJS await require('util').promisify(setTimeout)(3000)juga dapat dicapai tanpa memerlukan oleh:await setTimeout[Object.getOwnPropertySymbols(setTimeout)[0]](3000)
Shl
5
@Shl menarik. Saya pikir itu kurang dapat dibaca daripada solusi saya sekalipun. Jika orang tidak setuju saya dapat menambahkannya ke solusi?
Harry
2
Versi yang diperlukan jelas jauh lebih baik daripada getOwnPropertySymbolsversi ... jika tidak rusak ...!
Matt Fletcher
2
Hai yang disana @Harry. Tampaknya Anda memasukkan liner satu dari jawaban FlavourScape dalam jawaban Anda sendiri. Saya tidak ingin menganggap niat Anda, tetapi itu tidak benar-benar adil bagi mereka. Bisakah Anda mengembalikan suntingan Anda? Sekarang ini kelihatannya seperti plagiarisme ..
Félix Gagnon-Grenier
2
Saya telah menghapus satu baris karena jawabannya ada di bawah, namun saya telah melihat banyak jawaban populer memperbarui jawaban mereka untuk memasukkan jawaban baru lainnya karena sebagian besar pembaca tidak repot-repot melihat melewati beberapa tanggapan pertama.
Harry
130

Satu garis cepat, cara sejajar

 await new Promise(resolve => setTimeout(resolve, 1000));
FlavorScape
sumber
4
let sleep = ms => new Promise( r => setTimeout(r, ms));// fungsi satu liner
Soldeplata Saketos
8
lebih pendek :-)await new Promise(resolve => setTimeout(resolve, 5000))
Liran Brimer
1
apa artinya ketika kalian menggunakan "menyelesaikan" x 2 kali di baris yang sama? Seperti: tunggu Janji baru (resol = = setTimeout (resol, 1000)); apakah itu ref. untuk dirinya sendiri atau apa? Saya akan melakukan sesuatu seperti ini sebagai gantinya: function myFunc () {}; tunggu Janji baru (resolasikan => setTimeout (myFunc, 1000));
PabloDK
35

setTimeoutbukan asyncfungsi, jadi Anda tidak dapat menggunakannya dengan ES7 async-tunggu. Tetapi Anda bisa mengimplementasikan sleepfungsi Anda menggunakan ES6 Promise :

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

Maka Anda akan dapat menggunakan sleepfungsi baru ini dengan ES7 async-tunggu:

var fileList = await sleep(listFiles, nextPageToken)

Harap perhatikan bahwa saya hanya menjawab pertanyaan Anda tentang menggabungkan ES7 async / menunggu dengan setTimeout, meskipun mungkin tidak membantu menyelesaikan masalah Anda dengan mengirimkan terlalu banyak permintaan per detik.


Pembaruan: Versi node.js modern memiliki implementasi batas waktu async buid-in, dapat diakses melalui utilitas helper.

const {promisify} = require('util');
const setTimeoutAsync = promisify(setTimeout);
Leonid Beschastny
sumber
2
Anda seharusnya tidak melakukan itu, ketika fnmelempar kesalahan tidak akan tertangkap.
Bergi
@Bergi Saya pikir itu meluap ke new Promisemana Anda bisa sleep.catch.
Florian Wendelborn
3
@Dodekeract Tidak, ini dalam setTimeoutcallback asinkron dan new Promisecallback telah dilakukan sejak lama. Ini akan menggelembung ke konteks global dan dibuang sebagai pengecualian yang tidak ditangani.
Bergi
> masalah dengan mengirim terlalu banyak permintaan per detik. Anda ingin menggunakan "debounce" mungkin untuk mencegah hal-hal seperti UI menembakkan terlalu banyak ruquest.
FlavourScape
5

Jika Anda ingin menggunakan jenis sintaksis yang sama seperti setTimeoutAnda dapat menulis fungsi pembantu seperti ini:

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

Anda kemudian dapat menyebutnya seperti ini:

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

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

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

doStuffAsync();

Saya membuat intisari: https://gist.github.com/DaveBitter/f44889a2a52ad16b6a5129c39444bb57

Dave Bitter
sumber
1
nama fungsi seperti delayRunakan lebih masuk akal di sini, karena itu akan menunda jalannya fungsi panggil balik oleh X detik. Bukan contoh yang sangat menunggu, 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
vignesh
sumber
0

Kode berikut berfungsi di Chrome dan Firefox dan mungkin browser lainnya.

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

Tetapi di Internet Explorer saya mendapatkan Kesalahan Sintaks untuk "(resolve **=>** setTimeout..."

Berbayang
sumber
0

Membuat util terinspirasi dari Dave 's jawaban

Pada dasarnya diteruskan dalam donepanggilan balik untuk menelepon ketika operasi selesai.

// 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);
});

Ini adalah bagaimana saya menggunakannya:

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);
}
Astaga
sumber
0

Ini adalah versi saya dengan nodejs sekarang pada tahun 2020 di lab AWS

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)
    ...
}
zwitterion
sumber
-3

Ini adalah perbaikan yang lebih cepat dalam satu-liner.

Semoga ini bisa membantu.

// WAIT FOR 200 MILISECONDS TO GET DATA //
await setTimeout(()=>{}, 200);
Rommy Garg
sumber
1
Tidak bekerja Ini: await setTimeout(()=>{console.log('first')}, 200); console.log ('second')mencetak detik kemudian pertama
gregn3
1
@ gregn3 itu intinya ya. Ini adalah solusi non-pemblokiran di mana kode di luar fungsi dapat terus dijalankan sementara "operasi pemblokiran" diselesaikan di luar alur program utama. Meskipun sintaks yang Anda dan Rommy dan Mohamad berikan tidak sepenuhnya benar karena persyaratan untuk menunggu untuk diketuk dalam fungsi async (mungkin tambahan yang cukup baru), juga saya menggunakan node.js. Ini adalah solusi tweak saya. var test = async () => { await setTimeout(()=>{console.log('first')}, 1000); console.log ('second') }Saya telah memperpanjang batas waktu untuk menunjukkan kegunaannya.
azariah