Birçok Söz nasıl iade edilir ve başka şeyler yapmadan önce hepsini nasıl beklersiniz?


91

Eşzamansız olarak şeyler yapan bir yöntemi çağıran bir döngüm var. Bu döngü yöntemi birçok kez çağırabilir. Bu döngüden sonra, yalnızca tüm eşzamansız şeyler yapıldığında yürütülmesi gereken başka bir döngüm var.

Bu da ne istediğimi gösteriyor:

for (i = 0; i < 5; i++) {
    doSomeAsyncStuff();    
}

for (i = 0; i < 5; i++) {
    doSomeStuffOnlyWhenTheAsyncStuffIsFinish();    
}

Sözlere pek aşina değilim, bu yüzden birisi bana bunu başarmama yardım edebilir mi?

Benim doSomeAsyncStuff()davranışım şöyle:

function doSomeAsyncStuff() {
    var editor = generateCKEditor();
    editor.on('instanceReady', function(evt) {
        doSomeStuff();
        // There should be the resolve() of the promises I think.
    })
}

Belki bunun gibi bir şey yapmalıyım:

function doSomeAsyncStuff() {
    var editor = generateCKEditor();
    return new Promise(function(resolve,refuse) {
        editor.on('instanceReady', function(evt) {
            doSomeStuff();
            resolve(true);
        });
    });
}

Ama sözdiziminden emin değilim.


Asenkron aramaların kontrolü sizde mi? Zaten sözlerini geri veriyorlar mı yoksa onlara sözlerini geri verebilir misin?
TJ Crowder

Sıra tam olarak nedir? Önceki asenkron işlevlerin tümü bittikten sonra diğer işlevleri çağırmanız gerekiyor mu? Ya da her zaman uyumsuz bittikten sonra bir işlev çağırmanız mı gerekiyor?
Sosdoc

Şimdilik ilk işlev sözler vermiyor. Uygulamam gerektiğini. İşlevlerimin iş akışının bazı ayrıntılarını eklemek için mesajımı düzenlemek istiyorum. Ve evet, ikinci döngüdeki işleri yürütmeye başlamadan önce ilk döngüdeki tüm şeylerin bitmesine ihtiyacım var.
Ganbin

1
Düzenlemeniz yeniden: "Belki de böyle bir şey yapmalıyım" Evet, buna çok benziyor, ancak ssonunda yok Promise.
TJ Crowder

Yanıtlar:


169

Bunun için Promise.all( spec , MDN ) kullanabilirsiniz : Bir dizi bireysel sözü kabul eder ve size verdiğiniz tüm sözler çözüldüğünde çözülen veya herhangi biri reddedildiğinde reddedilen tek bir sözü geri verir.

Yani doSomeAsyncStuffbir söz verirseniz , o zaman:

    const promises = [];
//  ^^^^^−−−−−−−−−−−−−−−−−−−−−−−−−−− use `const` or `let`, not `var`
    
    for (let i = 0; i < 5; i++) {
//       ^^^−−−−−−−−−−−−−−−−−−−−−−−− added missing declaration
        promises.push(doSomeAsyncStuff());
    }
    
    Promise.all(promises)
        .then(() => {
            for (let i = 0; i < 5; i++) {
//               ^^^−−−−−−−−−−−−−−−− added missing declaration
                doSomeStuffOnlyWhenTheAsyncStuffIsFinish();    
            }
        })
        .catch((e) => {
            // handle errors here
        });

MDN'nin burada vaatlerle ilgili bir makalesi var . Ayrıca, JavaScript: The New Toys adlı kitabımın 8. Bölümünde , ilgileniyorsanız profilimdeki bağlantıları ayrıntılı olarak ele alıyorum.

İşte bir örnek:

 function doSomethingAsync(value) {
     return new Promise((resolve) => {
         setTimeout(() => {
             console.log("Resolving " + value);
             resolve(value);
         }, Math.floor(Math.random() * 1000));
     });
   }
   
   function test() {
       const promises = [];
       
       for (let i = 0; i < 5; ++i) {
           promises.push(doSomethingAsync(i));
       }
       
       Promise.all(promises)
           .then((results) => {
               console.log("All done", results);
           })
           .catch((e) => {
               // Handle errors here
           });
   }
   
   test();

Örnek çıktı ( Math.randomilk bitenler değişiklik gösterebileceğinden dolayı ):

Çözümleme 3
Çözümleme 2
Çözüm 1
4 çözümü
Çözümleme 0
Hepsi bitti [0,1,2,3,4]

Tamam teşekkürler Bunu şimdi deniyorum ve birkaç dakika içinde geri bildirimle geliyorum.
Ganbin

12
Vay canına, çok teşekkürler, şimdi vaatleri çok daha iyi anlıyorum, vaatler hakkında çok şey okudum, ama onları gerçek kodda kullanmamız gerekene kadar, tüm mekanizmaları gerçekten anlamıyoruz. Şimdi daha iyi anlıyorum ve senin sayende harika şeyler yazmaya başlayabilirim.
Ganbin

1
Ayrıca, bu görevleri herhangi bir nedenle sırayla tamamlamak istiyorsanız (örneğin, alay etme ilerlemesi), şu şekilde değiştirebilirsiniz Math.floor(Math.random() * 1000):(i * 1000)
Tamam

@TJ şimdi sonuç verilerini görünüme nasıl işleyebilirim ve orada verileri göstermek için döngüyü yapabilirim
Ajit Singh

1
@ user1063287 - Kod await, izin verilen bir bağlamdaysa bunu yapabilirsiniz . Şu anda kullanabileceğiniz tek yer awaitbir asyncişlevin iç kısmıdır. (Bir noktada, onu modüllerin en üst seviyesinde de kullanabileceksiniz.)
TJ Crowder

6

Yeniden kullanılabilir bir işlev bu model için iyi çalışıyor:

function awaitAll(count, asyncFn) {
  const promises = [];

  for (i = 0; i < count; ++i) {
    promises.push(asyncFn());
  }

  return Promise.all(promises);
}

OP örneği:

awaitAll(5, doSomeAsyncStuff)
  .then(results => console.log('doSomeStuffOnlyWhenTheAsyncStuffIsFinished', results))
  .catch(e => console.error(e));

İlgili bir desen, bir dizi üzerinde yineleme yapmak ve her öğe üzerinde bir zaman uyumsuz işlem gerçekleştirmektir:

function awaitAll(list, asyncFn) {
  const promises = [];

  list.forEach(x => {
    promises.push(asyncFn(x));
  });

  return Promise.all(promises);
}

Misal:

const books = [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }];

function doSomeAsyncStuffWith(book) {
  return Promise.resolve(book.name);
}

awaitAll(books, doSomeAsyncStuffWith)
  .then(results => console.log('doSomeStuffOnlyWhenTheAsyncStuffIsFinished', results))
  .catch(e => console.error(e));

1
Bu, kodu gerçekten daha kolay anlaşılır ve daha temiz hale getirir. Şu anki örneğin (açıkça OP'nin kurallarına uyarlanmış olan) bunu hak ettiğini düşünmüyorum. Bu harika bir numara, teşekkürler!
Shaun Vermaak

2
const doSomeAsyncStuff = async (funcs) => {
  const allPromises = funcs.map(func => func());
  return await Promise.all(allPromises);
}

doSomeAsyncStuff([
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
]);

2

İşte burada belirtilen cevapları anlamak için kendim için yazdığım kod. Bir for döngüsünde firavun faresi sorgularım var, bu yüzden asyncFunctiononun yerini almak için buraya koydum . Umarım herkese yardımcı olur. Bu komut dosyasını düğümde veya birçok Javascript çalışma zamanından herhangi birinde çalıştırabilirsiniz.

let asyncFunction = function(value, callback)
{
        setTimeout(function(){console.log(value); callback();}, 1000);
}



// a sample function run without promises

asyncFunction(10,
    function()
    {
        console.log("I'm back 10");
    }
);


//here we use promises

let promisesArray = [];

let p = new Promise(function(resolve)
{
    asyncFunction(20,
        function()
        {
            console.log("I'm back 20");
            resolve(20);
        }
    );
});

promisesArray.push(p);


for(let i = 30; i < 80; i += 10)
{
    let p = new Promise(function(resolve)
    {
        asyncFunction(i,
            function()
            {
                console.log("I'm back " + i);
                resolve(i);
            }
        );
    });
    promisesArray.push(p);
}


// We use Promise.all to execute code after all promises are done.

Promise.all(promisesArray).then(
    function()
    {
        console.log("all promises resolved!");
    }
)

1

/*** Worst way ***/
for(i=0;i<10000;i++){
  let data = await axios.get(
    "https://yourwebsite.com/get_my_data/"
  )
  //do the statements and operations
  //that are dependant on data
}

//Your final statements and operations
//That will be performed when the loop ends

//=> this approach will perform very slow as all the api call
// will happen in series


/*** One of the Best way ***/

const yourAsyncFunction = async (anyParams) => {
  let data = await axios.get(
    "https://yourwebsite.com/get_my_data/"
  )
  //all you statements and operations here
  //that are dependant on data
}
var promises = []
for(i=0;i<10000;i++){
  promises.push(yourAsyncFunction(i))
}
await Promise.all(promises)
//Your final statement / operations
//that will run once the loop ends

//=> this approach will perform very fast as all the api call
// will happen in parallal

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.