Zaman uyumsuzluk işlevi + bekleme + set kombinasyonu


307

Yeni eşzamansız özellikleri kullanmaya çalışıyorum ve umarım problemimi çözmek gelecekte başkalarına yardımcı olacaktır. Bu benim çalışan kod:

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

Sorun şu ki, while döngüm çok hızlı çalışıyor ve komut dosyası google API'sına saniyede çok fazla istek gönderiyor. Bu nedenle, talebi geciktiren bir uyku fonksiyonu oluşturmak istiyorum. Böylece bu işlevi diğer istekleri geciktirmek için de kullanabilirim. İsteği ertelemenin başka bir yolu varsa, lütfen bana bildirin.

Her neyse, bu benim yeni kod çalışmıyor. İsteğin yanıtı, setTimeout içinde anonim zaman uyumsuz işlevine döndürülür, ancak uyku işlevine verilen yanıtı nasıl döndürebileceğimi bilmiyorum. başlangıç ​​asyncGenerator işlevine.

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

Zaten bazı seçenekleri denedim: cevabı global bir değişkende saklamak ve uyku fonksiyonundan geri getirmek, anonim fonksiyon içinde geri arama yapmak vb.

Yanıtlar:


616

Kişisel sleepçünkü işlevi çalışmıyor setTimeout(henüz?) Olabilir bir söz dönmez awaited. Manuel olarak söz vermeniz gerekecek:

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

Btw, döngünüzü yavaşlatmak için büyük olasılıkla sleepgeri arama alan ve bu şekilde savunan bir işlev kullanmak istemezsiniz . Gibi bir şey yapmayı tercih ederim

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

Bu da hesaplamanın parentsen az 5 saniye sürmesini sağlar.


11
Promise.allYaklaşımı seviyorum . Çok basit ve zarif!
Anshul Koka

4
var [parents]temsil notasyonu neyi temsil eder? Daha önce görmedim ve google için zor bir şey
natedog

6
@NateUsher Dizi yıkıcı
Bergi

1
@tinkerr " beklenmesi gerekiyorsa zaman aşımı zaman uyumsuz olarak bildirilmelidir " - Hayır. Bir fonksiyonun sadece beklenebilecek bir söz vermesi gerekir (ya da aslında bir yenilebilirlik yeterlidir). Nasıl başardığı bu fonksiyonun uygulanmasına bağlıdır, bir olması gerekmez async function.
Bergi

2
@naisanza Hayır, async/ awaitedilir dayalı vaatler. Yerine koyduğu tek şey thençağrı.
Bergi

152

Düğüm 7.6'dan beripromisify , utils modülündeki fonksiyonlar fonksiyonunu aşağıdakilerle birleştirebilirsiniz :setTimeout() .

node.js

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

JavaScript

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

kullanım

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

1
Düğümde JS await require('util').promisify(setTimeout)(3000)ayrıca aşağıdakiler olmadan da elde edilebilir:await setTimeout[Object.getOwnPropertySymbols(setTimeout)[0]](3000)
Shl

5
İlginç @Shl. Ama benim çözümümden daha az okunabilir olduğunu düşünüyorum. Eğer insanlar aynı fikirde değilse, onu çözüme ekleyebilir miyim?
Harry

2
Gereken sürüm açıkça sürümden çok daha iyi getOwnPropertySymbols... eğer kırılmazsa ...!
Matt Fletcher

2
Hey, @Harry. Görünüşe göre FlavorScape'in cevabındaki tek astarı kendi cevabınıza dahil ettiniz. Niyetlerini varsaymak istemiyorum, ama bu onlar için gerçekten adil değil. Düzenlemenizi geri alabilir misiniz? Şu anda intihal gibi görünüyor ..
Félix Gagnon-Grenier

2
Cevap aşağıda olduğu gibi tek astarı kaldırdım, ancak birçok popüler yanıtın yanıtlarını diğer yeni cevapları içerecek şekilde güncellediğini gördüm, çünkü çoğu okuyucu ilk birkaç yanıtı geçmekten rahatsız olmuyor.
Harry

130

Hızlı tek astarlı, satır içi

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

4
let sleep = ms => new Promise( r => setTimeout(r, ms));// bir liner işlevi
Soldeplata Saketos

8
daha da kısa :-)await new Promise(resolve => setTimeout(resolve, 5000))
Liran Brimer

1
Aynı satırda 2 kez "çözmek" x kullandığınızda ne anlama gelir? Gibi: yeni Vaat bekliyor (resol => setTimeout (çözüm, 1000)); ref yapıyor mu? kendisi ya da ne? Bunun yerine böyle bir şey yapardım: function myFunc () {}; yeni Vaat bekliyor (resol => setTimeout (myFunc, 1000));
PabloDK

35

setTimeoutbir asyncişlev değildir , bu nedenle ES7 async-await ile kullanamazsınız. Ancak sleepES6 Promise kullanarak işlevinizi uygulayabilirsiniz :

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

Ardından bu yeni sleepişlevi ES7 zaman uyumsuz beklemede kullanabilirsiniz :

var fileList = await sleep(listFiles, nextPageToken)

Lütfen, yalnızca ES7 zaman uyumsuzluğunu / beklemesini birleştirmeyle ilgili sorunuzu yanıtladığımı unutmayınsetTimeout , ancak saniyede çok fazla istek gönderirken sorununuzu çözmenize yardımcı olmayabilir.


Güncelleştirme: Modern node.js sürümlerinde util.promisify yardımcısı aracılığıyla erişilebilen buid-in async zaman aşımı uygulaması var :

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

2
Bunu yapmamalısınız fn, hata atıldığında yakalanmazdı.
Bergi

@Bergi Sanırım new Promiseyapabileceğin yere kadar kabarıyor sleep.catch.
Florian Wendelborn

3
@Dodekeract Hayır, eşzamansız setTimeoutgeri aramada ve new Promisegeri arama uzun süredir yapıldı. Küresel bağlamda ortaya çıkacak ve işlenmemiş bir istisna olarak atılacaktır.
Bergi

> saniyede çok fazla istek gönderme sorunu. UI gibi şeylerin çok fazla ruquest yapmasını önlemek için "geri dönme" yi kullanmak istersiniz.
FlavorScape

5

Aynı tür bir sözdizimini kullanmak isterseniz, böyle setTimeoutbir yardımcı işlev yazabilirsiniz:

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

Daha sonra şöyle diyebilirsiniz:

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

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

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

doStuffAsync();

Bir özledim: https://gist.github.com/DaveBitter/f44889a2a52ad16b6a5129c39444bb57


1
delayRungeri çağırma işlevinin çalışmasını X saniye geciktireceği için burada gibi bir işlev adı daha anlamlı olacaktır. Pek beklenmeyen bir örnek değil, 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

Aşağıdaki kod Chrome ve Firefox ve diğer tarayıcılarda çalışır.

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

Ancak Internet Explorer'da bir sözdizimi hatası alıyorum. "(resolve **=>** setTimeout..."


0

Dan esinlenerek bir util Yapılan Dave 'in cevabı

Temel olarak done, işlem bittiğinde aramak için bir geri arama iletilir .

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

Ben nasıl kullanacağım:

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

Bu 2020 yılında AWS labdalarında nodejs ile benim versiyonum

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

Bu, tek astarda daha hızlı bir düzeltmedir.

Umarım bu yardımcı olur.

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

1
Çalışmıyor. Bu: önce ikinci sonra await setTimeout(()=>{console.log('first')}, 200); console.log ('second')basar
gregn3 30:19

1
@ gregn3 bu nokta evet. Bu, ana program akışının dışında bir "engelleme işlemi" tamamlanırken, işlevin dışındaki kodun yürütülmeye devam edebileceği engellenmeyen bir çözümdür. Siz ve Rommy ve Mohamad tarafından sağlanan sözdizimi, bir async işlevinde (oldukça yeni bir ek olabilir) beklemenin gerekliliği nedeniyle kesinlikle doğru olmasa da, node.js kullanıyorum. Bu benim düzeltilmiş çözümüm. var test = async () => { await setTimeout(()=>{console.log('first')}, 1000); console.log ('second') }Yararlı olduğunu göstermek için zaman aşımını uzattım.
azariah
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.