Async / await'i en üst düzeyde nasıl kullanabilirim?


188

Devam ediyorum async/ awaitve birkaç makalenin üzerinden geçtikten sonra işleri kendim test etmeye karar verdim. Ancak, neden bu işe yaramaz etrafında başımı sarmak gibi görünmüyor:

async function main() {  
    var value = await Promise.resolve('Hey there');
    console.log('inside: ' + value);
    return value;
}

var text = main();  
console.log('outside: ' + text);

Konsol aşağıdakileri çıkarır (düğüm v8.6.0):

> dış: [Söz Sözü]

> içeride: Hey orada

İşlev içindeki günlük iletisi neden daha sonra yürütülür? Ben neden async/ awaityaratıldı asenkron görevleri kullanarak senkronize yürütme gerçekleştirmek olduğunu düşündüm .

Ben kullanmadan işlevi içinde döndürülen değeri kullanabileceği bir yolu var mı .then()SONRA main()?


4
Hayır, yalnızca zaman makineleri eşzamansız kodu eşzamanlı hale getirebilir. awaitsöz thendizimi için şekerden başka bir şey değildir .
Bergi

Ana neden bir değer döndürüyor? Gerekirse, muhtemelen giriş noktası değildir ve başka bir işlev tarafından çağrılması gerekir (örn. Async IIFE).
Estus Flask

@estus, bir programın temsilcisini temsil etmeden, düğümde bir şeyleri test ederken sadece hızlı bir işlev adıydımain
Felipe

2
FYI, async/awaitES7'nin bir parçası, ES7 değil (ES2016)
Felix Kling

Yanıtlar:


271

Neden işe yaramıyor diye kafamı dolamıyorum.

Çünkü mainbir söz verir; tüm asyncfonksiyonlar.

En üst düzeyde:

  1. asyncAsla reddetmeyen bir üst düzey işlev kullanın ("işlenmeyen ret" hataları istemiyorsanız) veya

  2. Kullanın thenve catch, veya

  3. (Yakında!) Kullanımı üst düzeyawait , içinde Aşama 3 ulaşmış bir öneri sürecinin en üst düzey kullanımına izin awaitbir modülde.

# 1 - asyncAsla reddetmeyen en üst düzey işlev

(async () => {
    try {
        var text = await main();
        console.log(text);
    } catch (e) {
        // Deal with the fact the chain failed
    }
})();

Dikkat edin catch; Eğer gerekir gidiyor başka bir şey, çünkü söz ret / zaman uyumsuz istisnalar işlemek; onları aktaracak kimse yok. Eğer tercih ederseniz, aracılığıyla çağırmak sonucuna bunu yapabilir catchfonksiyonu (yerine try/ catchsözdizimi):

(async () => {
    var text = await main();
    console.log(text);
})().catch(e => {
    // Deal with the fact the chain failed
});

... bu biraz daha özlü (bu nedenle hoşuma gidiyor).

Veya, elbette, hataları işlemeyin ve sadece "işlenmeyen ret" hatasına izin verin.

# 2 - thenvecatch

main()
    .then(text => {
        console.log(text);
    })
    .catch(err => {
        // Deal with the fact the chain failed
    });

catchHatalar zincirinde veya oluşursa işleyicisi çağrılır thenişleyicisi. ( catchİşleyicinizin hataları işlemeyecek hiçbir şey kaydedilmediğinden hata atmadığından emin olun .)

Veya her iki argüman da then:

main().then(
    text => {
        console.log(text);
    },
    err => {
        // Deal with the fact the chain failed
    }
);

Bir ret işleyicisini kaydettiğimizi bir kez daha unutmayın. Ancak bu formda, thengeri aramalarınızın hiçbirinin hata atmadığından, bunları işlemek için hiçbir şey kaydedilmediğinden emin olun .

awaitBir modülde 3. sırada

awaitModül olmayan bir komut dosyasının en üst düzeyinde kullanamazsınız , ancak üst düzey awaitteklif ( Aşama 3 ) bunu bir modülün en üst düzeyinde kullanmanıza izin verir. asyncÜst düzey kodunuzun reddedilmesini (hata atmasını) istemediğiniz için üst düzey işlev sarıcısı (yukarıdaki # 1) kullanmaya benzer, çünkü işlenmeyen bir ret hatasına neden olur. Bu nedenle, işler yanlış gittiğinde işlenmemiş reddedilmek istemiyorsanız, # 1'de olduğu gibi, kodunuzu bir hata işleyicisine sarmak istersiniz:

// In a module, once the top-level `await` proposal lands
try {
    var text = await main();
    console.log(text);
} catch (e) {
    // Deal with the fact the chain failed
}

Bunu yaparsanız, modülünüzden içe aktarılan herhangi bir modülün söz verdiğinize kadar bekleyeceğini unutmayın await; üst seviyeyi kullanan bir modül awaitdeğerlendirildiğinde, temel olarak modül yükleyiciye ( asyncfonksiyonun yaptığı gibi) bir söz verir, bu da ona bağlı herhangi bir modülün gövdelerini değerlendirmeden önce bu sözün yerine getirilmesini bekler.


Bunu bir söz olarak düşünmek, fonksiyonun neden hemen geri döndüğünü açıklıyor. Üst düzey anonim bir zaman uyumsuz işlev yapmayı
Felipe

2
@Felipe: Evet, async/ awaitsözlerin etrafındaki sözdizimsel şekerdir (iyi tür şeker :-)). Bunu sadece bir söz vermeyi düşünmüyorsun ; aslında öyle. ( Ayrıntılar .)
TJ Crowder

1
@LukeMcGregor - Her ikisini de asyncilk seçenekle yukarıda gösterdim . Üst düzey işlev için, her iki şekilde de görebilirim (çoğunlukla asyncsürümdeki iki girinti seviyesi nedeniyle ).
TJ Crowder

3
@Felipe - Üst düzey awaitteklifin Aşama 3'e ulaştığı cevabını şimdi güncelledim . :-)
TJ Crowder

1
@SurajShrestha - Hayır. Ama yapmadığı bir sorun değil. :-)
TJ Crowder

7

Üst Düzeyawait 3. aşamaya taşındı, bu nedenle sorunuzun cevabı Async / beklemeyi en üst düzeyde nasıl kullanabilirim? sadece awaitçağrıyı şuraya eklemektir main():

async function main() {  
    var value = await Promise.resolve('Hey there');
    console.log('inside: ' + value);
    return value;
}

var text = await main();  
console.log('outside: ' + text)

Ya da sadece:

const text = await Promise.resolve('Hey there');
console.log('outside: ' + text)

Hala yalnızca Webpack@v5.0.0-alpha.15 adresinde mevcut olduğunu unutmayın .

Eğer ediyorsanız typescript kullanarak , bu 3.8'de indi .

v8 modüllerde destek ekledi .

Ayrıca Deno tarafından desteklenmektedir (gonzalo-bahamondez tarafından yorumlandığı gibi).


Oldukça havalı. Biz Düğüm uygulanması için herhangi bir yol haritası var mı
Felipe

Bilmiyorum, ancak çok yakında bir TypeScript ve Babel uygulaması göreceğiz. TypeScript ekibinin 3. aşama dil özelliklerini uygulama politikası vardır ve bir Babel eklentisi genellikle teklifleri test etmek için TC39 sürecinin bir parçası olarak oluşturulur. Bkz. Github.com/Microsoft/TypeScript/issues/…
Taro

Deno'da da mevcuttur (sadece js, daktilo hala desteklemiyor github.com/microsoft/TypeScript/issues/25988 ) deno.land bkz. Deno.news/issues/… .
Gonzalo Bahamondez

Sözdizimi Hatası: bekliyor yalnızca eşzamansız işlevinde geçerlidir
Sudipta Dhara

4

Bu sorunun asıl çözümü, ona farklı yaklaşmaktır.

Muhtemelen amacınız, tipik olarak bir uygulamanın en üst düzeyinde gerçekleşen bir çeşit başlatmadır.

Çözüm, uygulamanızın en üst düzeyinde yalnızca tek bir JavaScript ifadesi olmasını sağlamaktır. Uygulamanızın üst kısmında yalnızca bir deyim varsa, o zaman her yerde eşzamansız / beklemede kullanabilirsiniz (elbette normal sözdizimi kurallarına tabi)

Başka bir deyişle, üst düzeyinizin tamamını bir işlev içine sarın, böylece artık üst düzey olmayacak ve uygulamanın en üst düzeyinde async / await'in nasıl çalıştırılacağı sorusunu çözecektir - yapmazsınız.

Uygulamanızın en üst düzeyi aşağıdaki gibi görünmelidir:

import {application} from './server'

application();

1
Amacımın başlatılması olduğu konusunda haklısın. Veritabanı bağlantıları, veri çekme vs. gibi şeyler Bazı durumlarda uygulamanın geri kalanına devam etmeden önce bir kullanıcının verilerinin alınması gerekliydi. Aslına bakarsanız, bu application()zaman uyumsuz olmayı mı öneriyorsunuz ?
Felipe

1
Hayır, sadece uygulamanızın kökünde yalnızca bir JavaScript ifadesi varsa, o zaman sorununuz gittiğini söylüyorum - gösterildiği gibi üst düzey ifade senkronize değil. Sorun, async'i en üst düzeyde kullanmanın mümkün olmamasıdır - bu seviyede gerçekten beklemek için sabırsızlanabilirsiniz - bu nedenle en üst düzeyde sadece bir ifade varsa, bu sorunu kaldırdınız. Başlatma zaman uyumsuzluk kodunuz içe aktarılan bazı kodlarda artık kapalı ve bu nedenle zaman uyumsuzluk sorunsuz çalışacak ve uygulamanızın başlangıcında her şeyi başlatabilirsiniz.
Duke Dougal

1
DÜZELTME - uygulama zaman uyumsuz bir işlevdir.
Duke Dougal

4
Net değilim üzgünüm. Mesele şu ki, en üst düzeyde, zaman uyumsuz bir işlev beklemiyor ... JavaScript doğrudan bir sonraki ifadeye geçiyor, böylece init kodunuzun tamamlandığından emin olamıyorsunuz. Eğer uygulamanızın üstünde sadece tek bir ifade varsa, bu sadece önemli değil.
Duke Dougal

3

Mevcut cevaplar hakkında daha fazla bilgi vermek için:

Bir node.jsdosyanın içeriği şu anda bir işlev gövdesi oluşturmak üzere dize benzeri bir şekilde birleştirilir.

Örneğin, bir dosyanız varsa test.js:

// Amazing test file!
console.log('Test!');

Ardından node.js, aşağıdaki gibi görünen bir işlevi gizlice birleştirir:

function(require, __dirname, ... a bunch more top-level properties) {
  // Amazing test file!
  console.log('test!');
}

Dikkat edilmesi gereken en önemli şey, ortaya çıkan fonksiyonun bir asenkron fonksiyon DEĞİL olmasıdır. Yani terimi awaitdoğrudan içinde kullanamazsınız !

Ancak bu dosyada vaatlerle çalışmanız gerektiğini varsayalım, o zaman iki olası yöntem vardır:

  1. await Doğrudan işlevin içinde kullanmayın
  2. Kullanma await

Seçenek 1, yeni bir kapsam oluşturmamızı gerektirir (ve bu kapsam olabilir async, çünkü bunun üzerinde kontrolümüz vardır):

// Amazing test file!
// Create a new async function (a new scope) and immediately call it!
(async () => {
  await new Promise(...);
  console.log('Test!');
})();

Seçenek 2, nesne yönelimli söz API'sini kullanmamızı gerektirir (sözlerle çalışma konusunda daha az hoş ama aynı derecede işlevsel paradigma)

// Amazing test file!
// Create some sort of promise...
let myPromise = new Promise(...);

// Now use the object-oriented API
myPromise.then(() => console.log('Test!'));

Şahsen, eğer uygulanabilirse, node.js'nin varsayılan olarak kodu bir asyncişleve birleştireceğini umuyorum . Bu, bu baş ağrısından kurtulacaktır.


0

Üst düzey bekleme, yaklaşmakta olan EcmaScript standardının bir özelliğidir. Şu anda, TypeScript 3.8 ile kullanmaya başlayabilirsiniz (şu anda RC sürümünde).

TypeScript 3.8 Nasıl Kurulur

Aşağıdaki komutu kullanarak TypeScript 3.8'i npm'den yükleyerek kullanmaya başlayabilirsiniz :

$ npm install typescript@rc

Şu anda, rcen son typecript 3.8 sürümünü yüklemek için etiketi eklemeniz gerekir .


Ama o zaman nasıl kullanılacağını açıklamanız gerekecek mi?
raarts

-2

Yana main()ishal uyumsuz bir söz verir. Sonucu then()yöntemde almak zorundasınız . Ve then()iadeler de söz verdiği process.exit()için, programı sonlandırmak için aramalısınız .

main()
   .then(
      (text) => { console.log('outside: ' + text) },
      (err)  => { console.log(err) }
   )
   .then(() => { process.exit() } )

2
Yanlış. Tüm vaatler kabul edildiğinde veya reddedildiğinde ve ana iş parçacığında daha fazla kod çalışmadığında, işlem kendi kendine sona erer.

@Dev: normalde exit()bir hatanın olup olmadığını belirtmek için farklı değerler iletmek istersiniz .
9000

@ 9000 Evet, ama bu burada yapılmıyor ve 0 çıkış kodu varsayılan olduğu için dahil etmeye gerek yok

@ 9000 aslında, hata işleyicisi muhtemelen kullanıyor olmalıdırprocess.exit(1)
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.