Async / Await क्लास कंस्ट्रक्टर


169

फिलहाल, मैं async/awaitएक क्लास कंस्ट्रक्टर फ़ंक्शन के भीतर उपयोग करने का प्रयास कर रहा हूं । ऐसा इसलिए है कि मैं e-mailजिस इलेक्ट्रॉन परियोजना पर काम कर रहा हूं, उसके लिए एक कस्टम टैग मिल सकता है ।

customElements.define('e-mail', class extends HTMLElement {
  async constructor() {
    super()

    let uid = this.getAttribute('data-uid')
    let message = await grabUID(uid)

    const shadowRoot = this.attachShadow({mode: 'open'})
    shadowRoot.innerHTML = `
      <div id="email">A random email message has appeared. ${message}</div>
    `
  }
})

हालाँकि, प्रोजेक्ट निम्न त्रुटि के साथ काम नहीं करता है:

Class constructor may not be an async method

क्या इसको दरकिनार करने का कोई तरीका है ताकि मैं इसके भीतर async / प्रतीक्षा का उपयोग कर सकूं? कॉलबैक या .then () की आवश्यकता के बजाय?


6
एक निर्माता का उद्देश्य आपको एक वस्तु आवंटित करना और फिर तुरंत वापस करना है। क्या आप वास्तव में बहुत अधिक विशिष्ट हो सकते हैं कि आपको क्यों लगता है कि आपका कंस्ट्रक्टर async होना चाहिए? क्योंकि हम लगभग XY समस्या से निपटने की गारंटी दे रहे हैं।
माइक 'पोमैक्स' कमेरमन्स

4
@ माइक'पोमैक्स'कैमरमंस जो काफी संभव है। मूल रूप से, मुझे इस तत्व को लोड करने के लिए मेटाडेटा प्राप्त करने के लिए एक डेटाबेस को क्वेरी करने की आवश्यकता है। डेटाबेस को छोड़ना एक अतुल्यकालिक ऑपरेशन है, और इसलिए मुझे तत्व के निर्माण से पहले इसे पूरा करने के लिए कुछ तरीके की आवश्यकता होती है। मैं कॉलबैक का उपयोग नहीं करूंगा, क्योंकि मैंने परियोजना के बाकी हिस्सों में वेट / एसिंक्स का उपयोग किया है और निरंतरता रखना चाहूंगा।
अलेक्जेंडर क्रैग्स

@ Mike'Pomax'Kamermans इस का पूरा संदर्भ एक ईमेल क्लाइंट है, जहां प्रत्येक HTML तत्व विधि के <e-mail data-uid="1028"></email>उपयोग से जानकारी के साथ आबादी और वहां समान दिखता है customElements.define()
अलेक्जेंडर क्रैग्स

आप बहुत ज्यादा नहीं चाहते कि एक कंस्ट्रक्टर एसिंक्स हो। एक तुल्यकालिक निर्माता बनाएँ जो आपकी वस्तु लौटाता है और फिर एक विधि का उपयोग करता है जैसे .init()कि एसिंक्स सामान करना। इसके अलावा, चूंकि आप HTMLElement को उप-वर्ग कर रहे हैं, इसलिए यह बहुत संभावना है कि इस वर्ग का उपयोग करने वाले कोड का कोई पता नहीं है कि यह एक async बात है इसलिए आपको वैसे भी एक पूरे अलग समाधान की तलाश करने की संभावना है।
jfriend00

जवाबों:


263

यह कभी काम नहीं कर सकता ।

asyncकीवर्ड की अनुमति देता है awaitएक समारोह के रूप में चिह्नित में इस्तेमाल किया जाएगा async, लेकिन यह भी एक वादा जनरेटर में है कि समारोह बदल देता है। तो एक समारोह के साथ चिह्नित asyncएक वादा वापस आ जाएगा। दूसरी ओर एक कंस्ट्रक्टर जिस वस्तु का निर्माण कर रहा है, उसे वापस करता है। इस प्रकार हमारे पास एक ऐसी स्थिति है जहां आप एक वस्तु और एक वादा दोनों लौटना चाहते हैं: एक असंभव स्थिति।

आप केवल async / प्रतीक्षा का उपयोग कर सकते हैं जहाँ आप वादों का उपयोग कर सकते हैं क्योंकि वे मूल रूप से वादों के लिए वाक्यविन्यास चीनी हैं। आप एक निर्माता में वादों का उपयोग नहीं कर सकते क्योंकि एक निर्माणकर्ता को निर्माण की गई वस्तु वापस करनी चाहिए, एक वादा नहीं।

इसे दूर करने के लिए दो डिज़ाइन पैटर्न हैं, दोनों का आविष्कार वादे से पहले हुआ था।

  1. एक init()फ़ंक्शन का उपयोग । यह jQuery की तरह एक सा काम करता है .ready()। आपके द्वारा बनाई गई वस्तु का उपयोग केवल उसके स्वयं के initया readyकार्य के अंदर ही किया जा सकता है :

    उपयोग:

    var myObj = new myClass();
    myObj.init(function() {
        // inside here you can use myObj
    });

    कार्यान्वयन:

    class myClass {
        constructor () {
    
        }
    
        init (callback) {
            // do something async and call the callback:
            callback.bind(this)();
        }
    }
  2. एक बिल्डर का उपयोग करें। मैंने इसका उपयोग जावास्क्रिप्ट में बहुत अधिक नहीं देखा है, लेकिन यह जावा में अधिक सामान्य वर्क-अराउंड में से एक है जब किसी ऑब्जेक्ट को अतुल्यकालिक रूप से निर्माण करने की आवश्यकता होती है। बेशक, बिल्डर पैटर्न का उपयोग ऑब्जेक्ट का निर्माण करते समय किया जाता है जिसमें बहुत अधिक जटिल मापदंडों की आवश्यकता होती है। जो कि एसिंक्रोनस बिल्डरों के लिए उपयोग-मामला है। अंतर यह है कि एक async बिल्डर एक वस्तु वापस नहीं करता है, लेकिन उस वस्तु का एक वादा:

    उपयोग:

    myClass.build().then(function(myObj) {
        // myObj is returned by the promise, 
        // not by the constructor
        // or builder
    });
    
    // with async/await:
    
    async function foo () {
        var myObj = await myClass.build();
    }

    कार्यान्वयन:

    class myClass {
        constructor (async_param) {
            if (typeof async_param === 'undefined') {
                throw new Error('Cannot be called directly');
            }
        }
    
        static build () {
            return doSomeAsyncStuff()
               .then(function(async_result){
                   return new myClass(async_result);
               });
        }
    }

    Async / प्रतीक्षा के साथ कार्यान्वयन:

    class myClass {
        constructor (async_param) {
            if (typeof async_param === 'undefined') {
                throw new Error('Cannot be called directly');
            }
        }
    
        static async build () {
            var async_result = await doSomeAsyncStuff();
            return new myClass(async_result);
        }
    }

ध्यान दें: हालांकि ऊपर दिए गए उदाहरणों में हम async बिल्डर के लिए वादों का उपयोग करते हैं वे कड़ाई से आवश्यक नहीं बोल रहे हैं। आप बस एक बिल्डर को आसानी से लिख सकते हैं जो कॉलबैक स्वीकार करता है।


स्थिर कार्यों के अंदर कॉलिंग फ़ंक्शन पर ध्यान दें।

यह async कंस्ट्रक्टर्स के साथ कुछ भी करने के लिए कुछ भी नहीं है, लेकिन कीवर्ड के साथ thisवास्तव में क्या मतलब है (जो उन भाषाओं से आने वाले लोगों के लिए थोड़ा आश्चर्यचकित हो सकता है जो विधि नामों के ऑटो-रिज़ॉल्यूशन करते हैं, अर्थात, वे भाषाएँ जिन्हें thisकीवर्ड की आवश्यकता नहीं है )।

thisकीवर्ड instantiated वस्तु को दर्शाता है। वर्ग नहीं। इसलिए आप आम तौर पर thisस्थैतिक कार्यों के अंदर उपयोग नहीं कर सकते क्योंकि स्थैतिक फ़ंक्शन किसी भी वस्तु के लिए बाध्य नहीं है, लेकिन सीधे वर्ग के लिए बाध्य है।

यह कहना है, निम्नलिखित कोड में:

class A {
    static foo () {}
}

आप ऐसा नहीं कर सकते हैं:

var a = new A();
a.foo() // NOPE!!

इसके बजाय आपको इसे कॉल करने की आवश्यकता है:

A.foo();

इसलिए, निम्न कोड में त्रुटि होगी:

class A {
    static foo () {
        this.bar(); // you are calling this as static
                    // so bar is undefinned
    }
    bar () {}
}

इसे ठीक करने के लिए आप barया तो एक नियमित कार्य कर सकते हैं या एक स्थिर विधि:

function bar1 () {}

class A {
    static foo () {
        bar1();   // this is OK
        A.bar2(); // this is OK
    }

    static bar2 () {}
}

ध्यान दें कि टिप्पणियों के आधार पर, विचार यह है कि यह एक HTML तत्व है, जिसमें आमतौर पर एक मैनुअल नहीं होता है, init()लेकिन इसमें ( srcया hrefइस मामले में data-uid) , जिसका अर्थ है एक सेटर का उपयोग करके बंधे हुए कार्यक्षमता को बांधना हर बार एक नया मूल्य
बंधता है

आपको यह टिप्पणी करनी चाहिए कि नीचे का उत्तर अपर्याप्त क्यों है (यदि यह है)। या इसे अन्यथा संबोधित करें।
ऑगी गार्डनर

मैं उत्सुक हूं कि bindपहले उदाहरण में क्यों आवश्यक है callback.bind(this)();? ताकि आप this.otherFunc()कॉलबैक के भीतर जैसी चीजें कर सकें ?
अलेक्जेंडर क्रैग्स

1
@AlexanderCraggs यह सिर्फ सुविधा है ताकि thisकॉलबैक में संदर्भित हो myClass। यदि आप हमेशा myObjइसके बजाय उपयोग करते हैं, thisतो आपको इसकी आवश्यकता नहीं है
स्लीवेटमैन

1
वर्तमान में भाषा पर एक सीमा है, लेकिन मैं यह नहीं देखता कि भविष्य में आप const a = await new A()उसी तरह से क्यों नहीं हो सकते जैसे हमारे पास नियमित कार्य और async कार्य हैं।
7ynk3r

138

आप निश्चित रूप से ऐसा कर सकते हैं। मूल रूप से:

class AsyncConstructor {
    constructor() {
        return (async () => {

            // All async code here
            this.value = await asyncFunction();

            return this; // when done
        })();
    }
}

वर्ग उपयोग बनाने के लिए:

let instance = await new AsyncConstructor();

इस समाधान में हालांकि कुछ कम गिरावट है:

superनोट : यदि आपको उपयोग करने की आवश्यकता है super, तो आप इसे async कॉलबैक के भीतर नहीं कह सकते।

टाइपस्क्रिप्ट नोट: यह टाइपस्क्रिप्ट के साथ समस्या का कारण बनता है क्योंकि निर्माता Promise<MyClass>इसके बजाय टाइप रिटर्न देता है MyClass। इसे हल करने का कोई निश्चित तरीका नहीं है जिसे मैं जानता हूं। @Blitter द्वारा सुझाया गया एक संभावित तरीका कंस्ट्रक्टर बॉडी /** @type {any} */की शुरुआत में है- मुझे नहीं पता कि यह सभी परिस्थितियों में काम करता है या नहीं।


1
@PAStheLoD मुझे नहीं लगता कि यह वापसी के बिना वस्तु का समाधान करेगा, लेकिन आप कह रहे हैं कि यह ऐसा करता है कि मैं कल्पना और अपडेट की समीक्षा करूंगा ...
डाउनगेट

2
@JuanLanus async ब्लॉक स्वचालित रूप से पैरामीटर पैरामीटर को कैप्चर करेगा ताकि तर्क x के लिए आपको केवल करने की आवश्यकता होconstructor(x) { return (async()=>{await f(x); return this})() }
Downgoat

1
@PAStheLoD: return thisआवश्यक है, क्योंकि जब constructorयह आपके लिए स्वचालित रूप से हो जाता है, तो यह है कि async IIFE नहीं करता है, और आप एक खाली वस्तु वापस कर देंगे।
डैन डस्केल्सस्कु

1
वर्तमान में TS 3.5.1 के रूप में ES5, ES2017, ES2018 को लक्षित करना (और शायद अन्य, लेकिन मैंने जाँच नहीं की है) यदि आप एक निर्माता में वापसी करते हैं तो आपको यह त्रुटि संदेश मिलता है: "वापसी प्रकार का निर्माण हस्ताक्षर करने योग्य होना चाहिए उदाहरण वर्ग का प्रकार। " IIFE का प्रकार एक वादा <यह> है, और चूंकि वर्ग एक वादा <टी> नहीं है, इसलिए मैं यह नहीं देखता कि यह कैसे काम कर सकता है। ('आप ’के अलावा आप क्या लौटा सकते हैं?) तो इसका मतलब है कि दोनों रिटर्न अनावश्यक हैं। (बाहरी व्यक्ति थोड़ा खराब है, क्योंकि यह एक
जटिल

3
@PAStheLoD हाँ, यह एक टाइपस्क्रिप्ट सीमा है। आम तौर पर जेएस में एक वर्ग का निर्माण किया Tजाना चाहिए, Tलेकिन जब हम लौटते हैं, तो वह एसिंक्स क्षमता प्राप्त करने के Promise<T>लिए this, लेकिन वह टाइपस्क्रिप्ट को भ्रमित करता है । आपको बाहरी रिटर्न की आवश्यकता है अन्यथा आपको पता नहीं चलेगा जब वादा पूरा हो जाता है- परिणामस्वरूप यह दृष्टिकोण टाइपस्क्रिप्ट पर काम नहीं करेगा (जब तक कि कुछ प्रकार के साथ हैकिंग नहीं है?)। एक प्रकार का विशेषज्ञ नहीं है, हालांकि ऐसा नहीं बोल सकता है
डाउनगेट

7

क्योंकि async फ़ंक्शंस वादे हैं, आप अपने वर्ग पर एक स्थिर फ़ंक्शन बना सकते हैं जो एक async फ़ंक्शन निष्पादित करता है जो वर्ग का उदाहरण देता है:

class Yql {
  constructor () {
    // Set up your class
  }

  static init () {
    return (async function () {
      let yql = new Yql()
      // Do async stuff
      await yql.build()
      // Return instance
      return yql
    }())
  }  

  async build () {
    // Do stuff with await if needed
  }
}

async function yql () {
  // Do this instead of "new Yql()"
  let yql = await Yql.init()
  // Do stuff with yql instance
}

yql()

let yql = await Yql.init()एक async फ़ंक्शन से कॉल करें ।


5

आपकी टिप्पणियों के आधार पर, आपको शायद वही करना चाहिए जो परिसंपत्ति लोडिंग के साथ हर दूसरे HTMLElement करता है: परिणाम के आधार पर एक लोड या त्रुटि घटना उत्पन्न करने के लिए, निर्माता एक साइडलोडिंग कार्रवाई शुरू करें।

हां, इसका मतलब है कि वादों का उपयोग करना, लेकिन इसका अर्थ "हर दूसरे एचटीएमएल तत्व के समान चीजें करना" भी है, इसलिए आप अच्छी कंपनी में हैं। उदाहरण के लिए:

var img = new Image();
img.onload = function(evt) { ... }
img.addEventListener("load", evt => ... );
img.onerror = function(evt) { ... }
img.addEventListener("error", evt => ... );
img.src = "some url";

यह स्रोत परिसंपत्ति के एक अतुल्यकालिक भार को मारता है, जब वह सफल होता है, तो समाप्त होता है onloadऔर जब वह गलत होता है, में समाप्त होता है onerror। तो, अपनी कक्षा को भी ऐसा करें:

class EMailElement extends HTMLElement {
  constructor() {
    super();
    this.uid = this.getAttribute('data-uid');
  }

  setAttribute(name, value) {
    super.setAttribute(name, value);
    if (name === 'data-uid') {
      this.uid = value;
    }
  }

  set uid(input) {
    if (!input) return;
    const uid = parseInt(input);
    // don't fight the river, go with the flow
    let getEmail = new Promise( (resolve, reject) => {
      yourDataBase.getByUID(uid, (err, result) => {
        if (err) return reject(err);
        resolve(result);
      });
    });
    // kick off the promise, which will be async all on its own
    getEmail()
    .then(result => {
      this.renderLoaded(result.message);
    })
    .catch(error => {
      this.renderError(error);
    });
  }
};

customElements.define('e-mail', EmailElement);

और फिर आप इवेंट कॉल और शैडो डोम के साथ रेंडरऑल्ड / रेंडरइर कार्यों को डील करते हैं:

  renderLoaded(message) {
    const shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innerHTML = `
      <div class="email">A random email message has appeared. ${message}</div>
    `;
    // is there an ancient event listener?
    if (this.onload) {
      this.onload(...);
    }
    // there might be modern event listeners. dispatch an event.
    this.dispatchEvent(new Event('load', ...));
  }

  renderFailed() {
    const shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innerHTML = `
      <div class="email">No email messages.</div>
    `;
    // is there an ancient event listener?
    if (this.onload) {
      this.onerror(...);
    }
    // there might be modern event listeners. dispatch an event.
    this.dispatchEvent(new Event('error', ...));
  }

यह भी ध्यान दें कि मैंने आपका idएक में बदल दिया है class, क्योंकि जब तक आप किसी अजीब कोड को केवल <e-mail>एक पृष्ठ पर अपने तत्व का एक ही उदाहरण देने की अनुमति नहीं देते हैं, तब तक आप एक अद्वितीय पहचानकर्ता का उपयोग नहीं कर सकते हैं और फिर इसे तत्वों के एक समूह में असाइन कर सकते हैं।


2

मैंने यह परीक्षण-मामला @ डाउगाट के उत्तर के आधार पर बनाया।
यह NodeJS पर चलता है। यह डाउगोएट का कोड है जहां एक setTimeout()कॉल द्वारा एसिंक्स भाग प्रदान किया जाता है ।

'use strict';
const util = require( 'util' );

class AsyncConstructor{

  constructor( lapse ){
    this.qqq = 'QQQ';
    this.lapse = lapse;
    return ( async ( lapse ) => {
      await this.delay( lapse );
      return this;
    })( lapse );
  }

  async delay(ms) {
    return await new Promise(resolve => setTimeout(resolve, ms));
  }

}

let run = async ( millis ) => {
  // Instatiate with await, inside an async function
  let asyncConstructed = await new AsyncConstructor( millis );
  console.log( 'AsyncConstructor: ' + util.inspect( asyncConstructed ));
};

run( 777 );

मेरा उपयोग मामला किसी वेब एप्लिकेशन के सर्वर-साइड के लिए DAO है।
जैसा कि मैं डीएओ को देखता हूं, वे एक रिकॉर्ड प्रारूप से जुड़े हैं, मेरे मामले में एक कुक के लिए उदाहरण के लिए एक MongoDB संग्रह है।
एक रसोइया उदाहरण एक कुक के डेटा रखता है।
मेरे बेचैन दिमाग में मैं कुक के डीएओ को एक तर्क के रूप में प्रदान करने में सक्षम हो जाऊंगा, और इंस्टेंटेशन ऑब्जेक्ट का निर्माण करेगा और इसे कुक के डेटा के साथ पॉप्युलेट करेगा।
इस प्रकार निर्माण में async सामान चलाने की आवश्यकता है।
मैं लिखना चाहता था:

let cook = new cooksDAO( '12345' );  

उपलब्ध गुणों की तरह cook.getDisplayName()
इस समाधान के साथ मुझे क्या करना है:

let cook = await new cooksDAO( '12345' );  

जो आदर्श के समान है।
साथ ही, मुझे इसे एक asyncफंक्शन के अंदर करने की जरूरत है ।

मेरी B- योजना निर्माणकर्ता के डेटा लोडिंग को छोड़ने के लिए थी, जो init फ़ंक्शन का उपयोग करने के लिए @slebetman सुझाव पर आधारित है, और ऐसा कुछ करना है:

let cook = new cooksDAO( '12345' );  
async cook.getData();

जो नियम नहीं तोड़ता।


2

निर्माण में async विधि का उपयोग करें ???

constructor(props) {
    super(props);
    (async () => await this.qwe(() => console.log(props), () => console.log(props)))();
}

async qwe(q, w) {
    return new Promise((rs, rj) => {
        rs(q());
        rj(w());
    });
}

2

स्टॉपगैप समाधान

आप एक async init() {... return this;}विधि बना सकते हैं , तो इसके बजाय new MyClass().init()जब भी आप सामान्य रूप से सिर्फ कहेंगे new MyClass()

यह साफ नहीं है क्योंकि यह हर किसी पर निर्भर करता है जो आपके कोड का उपयोग करता है, और अपने आप को, हमेशा इस तरह की वस्तु को तुरंत करने के लिए। हालाँकि यदि आप केवल एक विशेष स्थान पर या अपने कोड में दो का उपयोग कर रहे हैं, तो यह ठीक हो सकता है।

एक महत्वपूर्ण समस्या हालांकि तब होती है क्योंकि ES में कोई प्रकार की प्रणाली नहीं है, इसलिए यदि आप इसे कॉल करना भूल जाते हैं, तो आप सिर्फ undefinedइसलिए लौटे हैं क्योंकि निर्माणकर्ता कुछ भी नहीं लौटाता है। उफ़। कुछ करना बेहतर होगा:

सबसे अच्छी बात यह होगी:

class AsyncOnlyObject {
    constructor() {
    }
    async init() {
        this.someField = await this.calculateStuff();
    }

    async calculateStuff() {
        return 5;
    }
}

async function newAsync_AsyncOnlyObject() {
    return await new AsyncOnlyObject().init();
}

newAsync_AsyncOnlyObject().then(console.log);
// output: AsyncOnlyObject {someField: 5}

फैक्टरी विधि समाधान (थोड़ा बेहतर)

हालाँकि तब आप गलती से नया AsyncOnlyObject कर सकते हैं, आपको शायद केवल Object.create(AsyncOnlyObject.prototype)सीधे उपयोग करने वाले फ़ैक्टरी फ़ंक्शन का निर्माण करना चाहिए :

async function newAsync_AsyncOnlyObject() {
    return await Object.create(AsyncOnlyObject.prototype).init();
}

newAsync_AsyncOnlyObject().then(console.log);
// output: AsyncOnlyObject {someField: 5}

हालाँकि, आप कई वस्तुओं पर इस पैटर्न का उपयोग करना चाहते हैं ... आप इसे एक डेकोरेटर के रूप में सार कर सकते हैं या कुछ (आप, उब) को परिभाषित करने के बाद कॉल कर सकते हैं postProcess_makeAsyncInit(AsyncOnlyObject), लेकिन यहां मैं उपयोग करने जा रहा हूं extendsक्योंकि यह उपवर्ग शब्दार्थ में फिट बैठता है (उपवर्ग माता-पिता वर्ग + अतिरिक्त हैं, इसमें उन्हें मूल वर्ग के डिज़ाइन अनुबंध का पालन करना चाहिए, और अतिरिक्त कार्य कर सकते हैं; यदि माता-पिता भी समान नहीं थे, तो एक async उपवर्ग अजीब होगा, क्योंकि इसे एक ही प्रारंभ नहीं किया जा सकता है; मार्ग):


सार समाधान (विस्तार / उपवर्ग संस्करण)

class AsyncObject {
    constructor() {
        throw new Error('classes descended from AsyncObject must be initialized as (await) TheClassName.anew(), rather than new TheClassName()');
    }

    static async anew(...args) {
        var R = Object.create(this.prototype);
        R.init(...args);
        return R;
    }
}

class MyObject extends AsyncObject {
    async init(x, y=5) {
        this.x = x;
        this.y = y;
        // bonus: we need not return 'this'
    }
}

MyObject.anew('x').then(console.log);
// output: MyObject {x: "x", y: 5}

(उत्पादन में उपयोग न करें: मैंने जटिल परिदृश्यों के माध्यम से नहीं सोचा है जैसे कि क्या यह खोजशब्द के लिए आवरण लिखने का उचित तरीका है।)


2

जैसा कि दूसरों ने कहा है, आप इसे काम कर सकते हैं।

जावास्क्रिप्ट classतों वास्तव में उनके पास से कुछ भी वापस आ सकता है constructor, यहां तक ​​कि एक अन्य वर्ग का उदाहरण भी। इसलिए, आप Promiseअपने वर्ग के कंस्ट्रक्टर से वापस लौट सकते हैं जो कि इसके वास्तविक उदाहरण का समाधान करता है।

नीचे एक उदाहरण है:

export class Foo {

    constructor() {

        return (async () => {

            // await anything you want

            return this; // Return the newly-created instance
        }).call(this);
    }
}

फिर, आप Fooइस तरह के उदाहरण बनाएंगे :

const foo = await new Foo();

1

यदि आप बच सकते हैं extend, तो आप सभी वर्गों को एक साथ रखने से बच सकते हैं और निर्माण रचना के रूप में फ़ंक्शन रचना का उपयोग कर सकते हैं । आप वर्ग सदस्यों के बजाय दायरे में चर का उपयोग कर सकते हैं:

async function buildA(...) {
  const data = await fetch(...);
  return {
    getData: function() {
      return data;
    }
  }
}

और सरल का उपयोग करें

const a = await buildA(...);

यदि आप टाइपस्क्रिप्ट या प्रवाह का उपयोग कर रहे हैं, तो आप कंस्ट्रक्टर के इंटरफ़ेस को भी लागू कर सकते हैं

Interface A {
  getData: object;
}

async function buildA0(...): Promise<A> { ... }
async function buildA1(...): Promise<A> { ... }
...

0

कॉल () का उपयोग करके बिल्डर पैटर्न पर बदलाव:

function asyncMethod(arg) {
    function innerPromise() { return new Promise((...)=> {...}) }
    innerPromise().then(result => {
        this.setStuff(result);
    }
}

const getInstance = async (arg) => {
    let instance = new Instance();
    await asyncMethod.call(instance, arg);
    return instance;
}

0

आप तुरंत एक अनाम async फ़ंक्शन को ला सकते हैं जो संदेश लौटाता है और इसे संदेश चर में सेट करता है। यदि आप इस पैटर्न से अपरिचित हैं, तो आप तुरंत आमंत्रित फ़ंक्शन अभिव्यक्तियों (IEFES) पर एक नज़र डालना चाहते हैं। यह एक आकर्षण की तरह काम करेगा।

var message = (async function() { return await grabUID(uid) })()

-1

@ slebetmen का स्वीकृत उत्तर अच्छी तरह से समझाता है कि यह काम क्यों नहीं करता है। उस उत्तर में प्रस्तुत दो पैटर्नों के अलावा, एक अन्य विकल्प केवल कस्टम एसिंक्स गेट्टर के माध्यम से अपने एसिंक्स गुणों को एक्सेस करना है। कंस्ट्रक्टर () तब संपत्तियों के एसिंक्स निर्माण को ट्रिगर कर सकता है, लेकिन यह देखने के लिए कि संपत्ति उपलब्ध है या इसका उपयोग करने से पहले यह देखने के लिए यह जाँच करता है कि गेट्टर।

यह दृष्टिकोण विशेष रूप से तब उपयोगी होता है जब आप स्टार्टअप पर एक बार एक वैश्विक वस्तु को इनिशियलाइज़ करना चाहते हैं, और आप इसे एक मॉड्यूल के अंदर करना चाहते हैं। अपने को इनिशियलाइज़ करने index.jsऔर उन जगहों पर उदाहरण देने के बजाय जिन्हें इसकी ज़रूरत है, बस requireअपने मॉड्यूल को जहाँ भी वैश्विक ऑब्जेक्ट की आवश्यकता हो।

प्रयोग

const instance = new MyClass();
const prop = await instance.getMyProperty();

कार्यान्वयन

class MyClass {
  constructor() {
    this.myProperty = null;
    this.myPropertyPromise = this.downloadAsyncStuff();
  }
  async downloadAsyncStuff() {
    // await yourAsyncCall();
    this.myProperty = 'async property'; // this would instead by your async call
    return this.myProperty;
  }
  getMyProperty() {
    if (this.myProperty) {
      return this.myProperty;
    } else {
      return this.myPropertyPromise;
    }
  }
}

-2

अन्य उत्तर स्पष्ट याद आ रहे हैं। बस अपने निर्माता से एक async फ़ंक्शन कॉल करें:

constructor() {
    setContentAsync();
}

async setContentAsync() {
    let uid = this.getAttribute('data-uid')
    let message = await grabUID(uid)

    const shadowRoot = this.attachShadow({mode: 'open'})
    shadowRoot.innerHTML = `
      <div id="email">A random email message has appeared. ${message}</div>
    `
}

यहां एक और "स्पष्ट" उत्तर की तरह , यह वह नहीं करेगा जो प्रोग्रामर आमतौर पर एक कंस्ट्रक्टर की अपेक्षा करता है, अर्थात जब ऑब्जेक्ट बनाया जाता है तो सामग्री सेट होती है।
Dan Dascalescu

2
@DanDascalescu यह सेट किया गया है, बस एसिंक्रोनस रूप से, जो वास्तव में प्रश्नकर्ता की आवश्यकता है। आपका कहना यह है कि जब वस्तु बनाई जाती है तो सामग्री समकालिक रूप से सेट नहीं होती है, जिसकी आवश्यकता प्रश्न द्वारा नहीं होती है। यही कारण है कि प्रश्न एक निर्माणकर्ता के भीतर से वेट / एसिंक्स का उपयोग करने के बारे में है। मैंने प्रदर्शित किया है कि आप एक निर्माण कार्य से एक async फ़ंक्शन को कॉल करके जितना आप चाहते हैं उतने प्रतीक्षित / async आमंत्रित कर सकते हैं। मैंने पूरी तरह से सवाल का जवाब दिया है।
Navigateur

@ नवगीतकार वही समाधान था जिसके साथ मैं आया था, लेकिन इसी तरह के एक और सवाल पर टिप्पणी यह बताती है कि इसे इस तरह से नहीं किया जाना चाहिए। एक वादा किया जा रहा है मुख्य समस्या कंस्ट्रक्टर में खो जाती है, और यह एंटीपैटर्न है। क्या आपके पास कोई संदर्भ है जहां यह आपके निर्माणकर्ता से एक async फ़ंक्शन को कॉल करने के इस दृष्टिकोण की सिफारिश करता है?
मार्कलर

1
@ मर्कर कोई संदर्भ नहीं, आपको किसी की आवश्यकता क्यों है? इससे कोई फर्क नहीं पड़ता कि अगर किसी चीज़ को "खो" दिया जाता है अगर आपको पहली जगह की ज़रूरत नहीं है। और अगर आपको वादे की ज़रूरत है, तो इसे जोड़ना this.myPromise =(सामान्य अर्थों में) तुच्छ है, इसलिए किसी भी मायने में विरोधी पैटर्न नहीं। निर्माण पर, एक async एल्गोरिथ्म को किक करने की आवश्यकता के लिए पूरी तरह से वैध मामले हैं, जिसका कोई वापसी मूल्य नहीं है, और हमें किसी भी तरह से सरल जोड़ना है, इसलिए जो कोई भी ऐसा नहीं करने की सलाह दे रहा है वह कुछ गलत समझ रहा है
नवगीतकार

1
जवाब देने का समय निकालने के लिए धन्यवाद। स्टाकेवरफ्लो पर यहाँ परस्पर विरोधी उत्तरों के कारण मैं आगे पढ़ने की तलाश कर रहा था। मैं इस परिदृश्य के लिए कुछ सर्वोत्तम प्रथाओं की पुष्टि करने की उम्मीद कर रहा था।
मार्केल

-2

आपको thenउदाहरण के लिए फ़ंक्शन जोड़ना चाहिए । Promiseयह Promise.resolveस्वचालित रूप से एक तबके के रूप में पहचान करेगा

const asyncSymbol = Symbol();
class MyClass {
    constructor() {
        this.asyncData = null
    }
    then(resolve, reject) {
        return (this[asyncSymbol] = this[asyncSymbol] || new Promise((innerResolve, innerReject) => {
            this.asyncData = { a: 1 }
            setTimeout(() => innerResolve(this.asyncData), 3000)
        })).then(resolve, reject)
    }
}

async function wait() {
    const asyncData = await new MyClass();
    alert('run 3s later')
    alert(asyncData.a)
}

innerResolve(this)काम नहीं करेगा, जैसा thisकि अभी भी एक उपयुक्त है। यह एक कभी न खत्म होने वाले पुनरावर्ती संकल्प की ओर जाता है।
बरगी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.