मैं शीर्ष स्तर पर async / प्रतीक्षा का उपयोग कैसे कर सकता हूं?


182

मैं बहुत से लेखों पर जा रहा हूं async/ awaitऔर कई लेखों के बाद मैंने खुद चीजों को परखने का फैसला किया है। हालाँकि, मैं अपना सिर इधर-उधर लपेटता नहीं दिख सकता कि यह क्यों काम नहीं करता है:

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

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

कंसोल निम्न आउटपुट (नोड v8.6.0):

> बाहर: [वस्तु वादा]

> अंदर: अरे वहाँ

फ़ंक्शन के अंदर लॉग संदेश बाद में क्यों निष्पादित होता है? मुझे लगा कि अतुल्यकालिक कार्यों का उपयोग करके तुल्यकालिक निष्पादन करने के लिए कारण async/ awaitबनाया गया था।

वहाँ एक तरह से मैं समारोह के अंदर दिए गए मूल्य का उपयोग कर सकता है एक के .then()बाद का उपयोग किए बिना है main()?


4
नहीं, केवल टाइम मशीनें एसिंक्रोनस कोड को सिंक्रोनस बना सकती हैं। awaitवादा thenवाक्यविन्यास के लिए चीनी के अलावा कुछ भी नहीं है ।
Bergi

मुख्य मूल्य वापस क्यों आता है? यदि यह होना चाहिए, तो शायद यह प्रवेश बिंदु नहीं है और इसे किसी अन्य फ़ंक्शन (जैसे async IIFE) द्वारा कॉल करने की आवश्यकता है।
एस्टुस फ्लास्क

@estus यह सिर्फ एक त्वरित कार्य नाम था जब मैं नोड में चीजों का परीक्षण कर रहा था, जरूरी नहीं कि एक कार्यक्रम का प्रतिनिधि होmain
फेलिप

2
FYI करें, async/awaitES2017 का हिस्सा है, ES7 (ES2016) का हिस्सा नहीं है
फेलिक्स क्लिंग

जवाबों:


267

मैं अपने सिर को चारों ओर लपेटने के लिए प्रतीत नहीं हो सकता कि यह क्यों काम नहीं करता है।

क्योंकि mainप्रतिज्ञा लौटाता है; सभी asyncकार्य करते हैं।

शीर्ष स्तर पर, आपको या तो होना चाहिए:

  1. एक शीर्ष-स्तरीय asyncफ़ंक्शन का उपयोग करें जो कभी भी अस्वीकार नहीं करता है (जब तक कि आप "अनहेल्ड रिजेक्शन" त्रुटियां नहीं चाहते हैं), या

  2. का प्रयोग करें thenऔर catch, या

  3. (जल्द ही आ रहा है!) शीर्ष-स्तर काawait उपयोग करें , एक प्रस्ताव जो इस प्रक्रिया में स्टेज 3 तक पहुंच गया है जो awaitएक मॉड्यूल में शीर्ष-स्तरीय उपयोग की अनुमति देता है ।

# 1 - शीर्ष-स्तरीय asyncफ़ंक्शन जो कभी अस्वीकार नहीं करता है

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

सूचना catch; आपको वादे के खारिज / async अपवादों को संभालना चाहिए , क्योंकि कुछ भी नहीं होने जा रहा है; आपके पास उन्हें पास करने के लिए कोई कॉलर नहीं है। यदि आप चाहें, तो आप इसे catchफ़ंक्शन के माध्यम से कॉल करने के परिणाम पर कर सकते हैं (बजाय try/ catchवाक्यविन्यास):

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

... जो थोड़ा अधिक संक्षिप्त है (मुझे उस कारण से पसंद है)।

या, ज़ाहिर है, त्रुटियों को संभालना नहीं है और बस "अनहेल्ड रिजेक्ट" त्रुटि की अनुमति दें।

# 2 - thenऔरcatch

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

catchयदि त्रुटियों श्रृंखला में या अपने में होते हैं हैंडलर बुलाया जाएगा thenहैंडलर। (सुनिश्चित करें कि आपका catchहैंडलर त्रुटियों को न फेंके, क्योंकि उन्हें संभालने के लिए कुछ भी पंजीकृत नहीं है।)

या दोनों तर्क then:

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

फिर से ध्यान दें कि हम एक अस्वीकृति हैंडलर को पंजीकृत कर रहे हैं। लेकिन इस रूप में, सुनिश्चित करें कि आपके thenकिसी भी कॉलबैक में कोई त्रुटि नहीं है, उन्हें संभालने के लिए कुछ भी पंजीकृत नहीं है।

awaitएक मॉड्यूल में # 3 शीर्ष-स्तर

आप awaitएक गैर-मॉड्यूल स्क्रिप्ट के शीर्ष स्तर पर उपयोग नहीं कर सकते हैं , लेकिन शीर्ष-स्तरीय awaitप्रस्ताव ( स्टेज 3 ) आपको एक मॉड्यूल के शीर्ष स्तर पर इसका उपयोग करने की अनुमति देता है। यह एक शीर्ष-स्तरीय asyncफ़ंक्शन आवरण (# 1 ऊपर) का उपयोग करने के समान है , जिसमें आप अपने शीर्ष-स्तरीय कोड को अस्वीकार नहीं करना चाहते हैं (त्रुटि फेंकना) क्योंकि इसके परिणामस्वरूप अनहेल्ड अस्वीकृति त्रुटि होगी। इसलिए जब तक आप उस अनहेल्ड रिजेक्शन को नहीं चाहते हैं जब चीजें गलत हो जाती हैं, जैसा कि # 1 के साथ होता है, आप अपने कोड को किसी एरलर में लपेटना चाहेंगे:

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

ध्यान दें कि यदि आप ऐसा करते हैं, तो आपके मॉड्यूल से आयात होने वाला कोई भी मॉड्यूल इंतजार करेगा जब तक कि आप वादों को निपटा नहीं लेते await; जब शीर्ष-स्तर awaitका उपयोग करने वाले मॉड्यूल का मूल्यांकन किया जाता है, तो यह मूल रूप से मॉड्यूल लोडर (जैसे कोई asyncफ़ंक्शन करता है) को एक वादा लौटाता है , जो तब तक इंतजार करता है जब तक कि उस पर निर्भर किसी भी मॉड्यूल के निकायों का मूल्यांकन करने से पहले उस वादे का निपटान नहीं किया जाता है।


इसे एक वादे के रूप में सोचते हुए अब बताते हैं कि फ़ंक्शन तुरंत क्यों लौटता है। मैंने एक शीर्ष-स्तरीय अनाम async फ़ंक्शन बनाने के साथ प्रयोग किया और मुझे परिणाम मिले जो अब समझ में आते हैं
फेलिप

2
@ फेलिप: हां, async/ awaitवायदों के आसपास वाक्यविन्यास चीनी है (चीनी का अच्छा प्रकार :-))। आप सिर्फ एक वादा वापस करने के बारे में नहीं सोच रहे हैं ; यह वास्तव में करता है। ( विवरण ।)
टीजे क्राउडर

1
@LukeMcGregor - मैंने ऊपर, ऑल asyncऑप्शन के साथ दिखाया । शीर्ष-स्तरीय फ़ंक्शन के लिए, मैं इसे या तो तरीके से देख सकता हूं (ज्यादातर asyncसंस्करण पर इंडेंटेशन के दो स्तरों के कारण )।
टीजे क्राउडर

3
@ फेलिप - मैंने अब जवाब अपडेट कर दिया है कि शीर्ष स्तर का awaitप्रस्ताव स्टेज 3 तक पहुंच गया है। :-)
टीजे क्राउडर

1
@SurajShrestha - नहीं, लेकिन यह कोई समस्या नहीं है कि यह नहीं है। :-)
टीजे क्राउडर

7

टॉप-लेवलawait चरण 3 में चला गया है, इसलिए आपके प्रश्न का उत्तर मैं शीर्ष स्तर पर एसिंक्स / वेट का उपयोग कैसे कर सकता हूं? इसमें केवल awaitकॉल जोड़ना है 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)

या केवल:

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

ध्यान रखें कि यह अभी भी केवल Webpack@v5.0.0-alpha.15 में उपलब्ध है ।

यदि आप टाइपस्क्रिप्ट का उपयोग कर रहे हैं , तो यह 3.8 में उतरा

v8 ने मॉड्यूल में समर्थन जोड़ा है

यह भी द्वारा समर्थित है Deno (के रूप में गोंजालो-bahamondez द्वारा टिप्पणी की)।


बहुत अच्छा। क्या हमारे पास नोड कार्यान्वयन के लिए कोई रोडमैप है
फेलिप

पता नहीं है, लेकिन यह बहुत संभावना है कि हम बहुत जल्द ही टाइपस्क्रिप्ट और बैबल कार्यान्वयन देखेंगे। टाइपस्क्रिप्ट टीम के पास चरण -3 भाषा सुविधाओं को लागू करने की नीति है, और एक बैबल प्लगइन आमतौर पर प्रस्तावों का परीक्षण करने के लिए TC39 प्रक्रिया के भाग के रूप में बनाया गया है। देखें github.com/Microsoft/TypeScript/issues/...
तारो

यह बहुत Deno में उपलब्ध है (बस जे एस, टाइपप्रति अभी भी यह समर्थन नहीं करता github.com/microsoft/TypeScript/issues/25988 ) deno.land देख deno.news/issues/...
गोंजालो बहामोंडेज़

SyntaxError: async फ़ंक्शन में केवल प्रतीक्षा ही मान्य है
Sudipta Dhara

4

इस समस्या का वास्तविक समाधान अलग तरीके से प्राप्त करना है।

संभवतः आपका लक्ष्य किसी प्रकार का प्रारंभ है जो आमतौर पर किसी एप्लिकेशन के शीर्ष स्तर पर होता है।

इसका समाधान यह सुनिश्चित करना है कि आपके आवेदन के शीर्ष स्तर पर केवल एक ही जावास्क्रिप्ट स्टेटमेंट है। यदि आपके पास अपने आवेदन के शीर्ष पर केवल एक बयान है, तो आप हर दूसरे बिंदु पर कहीं भी async / प्रतीक्षा का उपयोग करने के लिए स्वतंत्र हैं (पाठ्यक्रम के सामान्य वाक्यविन्यास नियमों के अधीन)

एक और तरीका रखो, अपने पूरे शीर्ष स्तर को एक फ़ंक्शन में लपेटो ताकि यह अब शीर्ष स्तर न हो और यह सवाल हल करता है कि कैसे async को चलाना है / किसी एप्लिकेशन के शीर्ष स्तर पर प्रतीक्षा करें - आप नहीं।

यह आपके आवेदन का शीर्ष स्तर कैसा होना चाहिए:

import {application} from './server'

application();

1
आप सही हैं कि मेरा लक्ष्य प्रारंभिक है। डेटाबेस कनेक्शन, डेटा पुल आदि जैसी चीजें। कुछ मामलों में बाकी एप्लिकेशन के साथ आगे बढ़ने से पहले उपयोगकर्ता का डेटा प्राप्त करना आवश्यक था। अनिवार्य रूप से आप प्रस्ताव कर रहे हैं कि application()async हो?
फेलिप

1
नहीं, मैं सिर्फ यह कह रहा हूं कि यदि आपके आवेदन के मूल में केवल एक जावास्क्रिप्ट स्टेटमेंट है तो आपका मुद्दा चला गया है - जैसा कि दिखाया गया शीर्ष स्तर का स्टेटमेंट async नहीं है। समस्या यह है कि शीर्ष स्तर पर async का उपयोग करना संभव नहीं है - आप वास्तव में उस स्तर पर प्रतीक्षा करने के लिए इंतजार नहीं कर सकते - इसलिए यदि शीर्ष स्तर पर केवल एक ही बयान है तो आपने उस मुद्दे को दरकिनार कर दिया है। आपका इनिशियलाइज़ async कोड अब कुछ इम्पोर्टेड कोड में डाउन हो गया है और इसलिए async बस ठीक काम करेगा, और आप अपने एप्लिकेशन की शुरुआत में सब कुछ इनिशियलाइज़ कर सकते हैं।
ड्यूक डगल

1
सुधार - अनुप्रयोग एक async फ़ंक्शन है।
ड्यूक डगल

4
मुझे खेद नहीं हो रहा है। मुद्दा यह है कि आमतौर पर, शीर्ष स्तर पर, एक एसिंक्स फ़ंक्शन का इंतजार नहीं किया जाता है .... जावास्क्रिप्ट सीधे अगले बयान पर जाता है ताकि आप निश्चित नहीं हो सकें कि आपका init कोड पूरा हो गया है। यदि आपके आवेदन के शीर्ष पर केवल एक ही कथन है कि बस कोई फर्क नहीं पड़ता है।
ड्यूक डगल

3

वर्तमान उत्तरों के शीर्ष पर कुछ और जानकारी देने के लिए:

एक node.jsफ़ाइल की सामग्री वर्तमान में एक समारोह निकाय बनाने के लिए, स्ट्रिंग-जैसे तरीके से संक्षिप्त की जाती है।

उदाहरण के लिए यदि आपके पास कोई फ़ाइल है test.js:

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

फिर node.jsचुपके से एक फंक्शन को देखेगा जो दिखता है:

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

ध्यान देने वाली मुख्य बात, यह है कि परिणामी फ़ंक्शन एक async फ़ंक्शन नहीं है। तो आप awaitइसके अंदर सीधे शब्द का उपयोग नहीं कर सकते हैं !

लेकिन कहते हैं कि आपको इस फ़ाइल में वादों के साथ काम करने की आवश्यकता है, तो दो संभावित तरीके हैं:

  1. await सीधे फ़ंक्शन के अंदर का उपयोग न करें
  2. उपयोग न करें await

विकल्प 1 के लिए हमें एक नया स्कोप बनाने की आवश्यकता है (और यह स्कोप हो सकता है async, क्योंकि हमारा इस पर नियंत्रण है):

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

विकल्प 2 में हमें ऑब्जेक्ट-ओरिएंटेड वादा एपीआई (वादों के साथ काम करने का कम सुंदर लेकिन समान रूप से कार्यात्मक प्रतिमान) का उपयोग करने की आवश्यकता है

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

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

मुझे व्यक्तिगत रूप से उम्मीद है कि, अगर यह काम करने योग्य है, तो नोड.जेएस डिफ़ॉल्ट रूप से एक asyncफ़ंक्शन में कोड को बदल देगा । कि इस सिरदर्द से छुटकारा मिलेगा।


0

शीर्ष-स्तरीय प्रतीक्षा आगामी EcmaScript मानक की एक विशेषता है। वर्तमान में, आप इसे टाइपस्क्रिप्ट 3.8 (इस समय आरसी संस्करण में) के साथ उपयोग करना शुरू कर सकते हैं।

टाइपस्क्रिप्ट 3.8 को कैसे स्थापित करें

आप निम्न आदेश का उपयोग करके इसे npm से इंस्टॉल करके टाइपस्क्रिप्ट 3.8 का उपयोग करना शुरू कर सकते हैं :

$ npm install typescript@rc

इस समय, आपको rcनवीनतम टाइपस्क्रिप्ट 3.8 संस्करण को स्थापित करने के लिए टैग जोड़ने की आवश्यकता है ।


लेकिन आपको यह समझाने की आवश्यकता होगी कि इसका उपयोग कैसे करें?
raarts

-2

चूंकि main()यह एसिंक्रोनस रूप से चलता है इसलिए यह एक वादा लौटाता है। आपको then()विधि में परिणाम प्राप्त करना होगा । और क्योंकि then()रिटर्न वादे भी, आपको process.exit()कार्यक्रम को समाप्त करने के लिए कॉल करना होगा।

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

2
गलत। एक बार सभी वादों को स्वीकार कर लिया गया या अस्वीकार कर दिया गया और मुख्य धागे में कोई और कोड नहीं चल रहा है, प्रक्रिया अपने आप समाप्त हो जाती है।

@Dev: आम तौर पर आप यह exit()संकेत देने के लिए विभिन्न मान पास करना चाहेंगे कि क्या त्रुटि हुई है।
9000

@ 9000 हां, लेकिन यह यहां नहीं किया जा रहा है, और चूंकि 0 का एक निकास कोड डिफ़ॉल्ट है, इसलिए इसे शामिल करने की कोई आवश्यकता नहीं है

@ 9000 वास्तव में, त्रुटि हैंडलर संभवतः उपयोग करना चाहिएprocess.exit(1)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.