मैं कोणीय में ऑब्जर्वेबल / http / async कॉल से प्रतिक्रिया कैसे वापस कर सकता हूं?


110

मेरे पास सेवा है जो एक अवलोकन योग्य रिटर्न देती है जो मेरे सर्वर से http अनुरोध करता है और डेटा प्राप्त करता है। मैं इस डेटा का उपयोग करना चाहता हूं लेकिन मैं हमेशा प्राप्त करना चाहता हूं undefined। समस्या क्या है?

सेवा :

@Injectable()
export class EventService {

  constructor(private http: Http) { }

  getEventList(): Observable<any>{
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });

    return this.http.get("http://localhost:9999/events/get", options)
                .map((res)=> res.json())
                .catch((err)=> err)
  }
}

घटक:

@Component({...})
export class EventComponent {

  myEvents: any;

  constructor( private es: EventService ) { }

  ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
        });

    console.log(this.myEvents); //This prints undefined!
  }
}

मैंने जाँच की कि मैं एसिंक्रोनस कॉल से प्रतिक्रिया कैसे वापस करूँ? पोस्ट करें, लेकिन कोई समाधान नहीं ढूंढ सका


2
यह इस तथ्य पर जोर देने के लिए एक अच्छा बिंदु होगा कि यह एसाइक्रोनस ऑपरेशन को सिन्क्रोनस-वन में बदलना संभव नहीं है।
n00dl3

@ n00dl3 टिप के लिए धन्यवाद! मैंने "आप क्या नहीं करना चाहिए:" खंड में समझाने की कोशिश की है। इसे संपादित करने के लिए स्वतंत्र महसूस करें।
एको


@HereticMonkey उस पोस्ट को पहले से ही उत्तर में श्रेय दिया जाता है
एको

@ सेको और? क्या यह सवाल किसी डुप्लिकेट से कम है?
हेरिटिक बंदर

जवाबों:


117

कारण:

इसका कारण यह undefinedहै कि आप एक अतुल्यकालिक ऑपरेशन कर रहे हैं। इसका मतलब यह है कि getEventListविधि को पूरा करने में कुछ समय लगेगा (ज्यादातर आपके नेटवर्क की गति पर निर्भर करता है)।

तो आइए http कॉल देखें।

this.es.getEventList()

आपके द्वारा वास्तव में ("अग्नि") बनाने के बाद आपका http अनुरोध आपसे प्रतिक्रिया की प्रतीक्षा मेंsubscribe रहेगा । प्रतीक्षा करते समय, जावास्क्रिप्ट इस कोड के नीचे की लाइनों को निष्पादित करेगा और यदि यह तुल्यकालिक असाइनमेंट / ऑपरेशन का सामना करता है तो यह उन्हें तुरंत निष्पादित करेगा।

इसलिए सदस्यता लेने getEventList()और प्रतिक्रिया की प्रतीक्षा करने के बाद ,

console.log(this.myEvents);

लाइन को तुरंत निष्पादित किया जाएगा। और इसका मूल्य undefinedसर्वर से प्रतिक्रिया आने से पहले है (या जो कुछ भी आपने इसे पहली बार में इनिशियलाइज़ किया है)।

यह करने के लिए समान है:

ngOnInit(){
    setTimeout(()=>{
        this.myEvents = response;
    }, 5000);

    console.log(this.myEvents); //This prints undefined!
}


उपाय:

तो हम इस समस्या को कैसे दूर करेंगे? हम कॉलबैक फ़ंक्शन का उपयोग करेंगे जो कि subscribeविधि है। क्योंकि जब डेटा सर्वर से आता है तो यह subscribeप्रतिक्रिया के साथ अंदर होगा ।

इसलिए कोड को बदलकर:

this.es.getEventList()
    .subscribe((response)=>{
        this.myEvents = response;
        console.log(this.myEvents); //<-- not undefined anymore
    });

प्रतिक्रिया मुद्रित करेगा .. कुछ समय बाद।


तुम्हे क्या करना चाहिए:

आपके लॉग इन करने के अलावा आपकी प्रतिक्रिया के साथ बहुत सी चीजें हो सकती हैं; subscribeडेटा आने पर आपको कॉलबैक के अंदर ( फंक्शन के अंदर ) ये सारे ऑपरेशन करने चाहिए ।

उल्लेख करने के लिए एक और बात यह है कि यदि आप एक Promiseपृष्ठभूमि से आते हैं , तो thenकॉलबैक पर्यवेक्षकों के subscribeसाथ मेल खाता है ।


आपको क्या नहीं करना चाहिए:

आपको एक async ऑपरेशन को सिंक ऑपरेशन में बदलने की कोशिश नहीं करनी चाहिए (यह नहीं कि आप कर सकते हैं)। उन कारणों में से एक जो हमारे पास async ऑपरेशंस हैं, उपयोगकर्ता को किसी ऑपरेशन के पूरा होने की प्रतीक्षा नहीं करना है जबकि वे उस समय की अन्य चीजों को कर सकते हैं। मान लीजिए कि आपके एक async ऑपरेशंस को पूरा होने में 3 मिनट लगते हैं, अगर हमारे पास async ऑपरेशंस नहीं हैं तो इंटरफ़ेस 3 मिनट के लिए फ्रीज़ हो जाएगा।


सुझाया गया पढ़ना:

इस उत्तर का मूल श्रेय इस बात को जाता है: मैं एसिंक्रोनस कॉल से प्रतिक्रिया कैसे लौटाऊं?

लेकिन कोणीय 2 रिलीज के साथ हमें टाइपस्क्रिप्ट और वेधशालाओं के लिए पेश किया गया था, इसलिए यह जवाब उम्मीद के मुताबिक पर्यवेक्षकों के साथ एक अतुल्यकालिक अनुरोध को संभालने की मूल बातें शामिल करता है।


3
जब प्रश्नकर्ता द्वारा पोस्ट के ठीक उसी समय पर एक प्रश्न का उत्तर दिया जाता है .. तो इसके बजाय ब्लॉग पोस्ट को रोकने और लिखने के लिए एक अच्छी जगह है
जोनास प्रैम

3
@JonasPraem सच है, लेकिन Stackoverflow पर ज्ञान साझा करने में कुछ भी गलत नहीं है। जैसा कि आप वोटों की संख्या देखते हैं, इसने यहां कई लोगों की मदद की और यह ऐसा करना जारी रखेगा।
अमित चिगदानी

11

कोणीय / जावास्क्रिप्ट में http कॉल करना अतुल्यकालिक ऑपरेशन है। इसलिए जब आप http कॉल करते हैं तो यह कॉल समाप्त करने के लिए नया थ्रेड असाइन करेगा और किसी अन्य थ्रेड के साथ अगली पंक्ति को निष्पादित करना शुरू करेगा। यही कारण है कि आपको अपरिभाषित मूल्य मिल रहा है। इसलिए इसे हल करने के लिए नीचे परिवर्तन करें

this.es.getEventList()  
      .subscribe((response)=>{  
       this.myEvents = response;  
        console.log(this.myEvents); //<-this become synchronous now  
    });

5

यदि आप केवल टेम्पलेट में myEvents का उपयोग करते हैं तो आप asyncPipe का उपयोग कर सकते हैं ।

यहाँ asyncPipe और Angular4 HttpClient उदाहरण के साथ उदाहरण है


1
और (!) इस कार्यान्वयन के साथ सदस्यता को सदस्यता समाप्त करने की आवश्यकता नहीं है: मध्यम.com/@sub.metu/…
सैन जे फाल्कन

@SanJayFalcon चूंकि यह http अनुरोध है, इसलिए इसमें सदस्यता रद्द करने की कोई आवश्यकता नहीं है।
AJT82

3

वेधशालाएं आलसी हैं इसलिए आपको मूल्य प्राप्त करने के लिए सदस्यता लेनी होगी। आपने इसे अपने कोड में ठीक से सब्सक्राइब किया था, लेकिन साथ ही साथ 'सब्सक्राइब' ब्लॉक के बाहर आउटपुट लॉग किया। इसलिए यह 'अपरिभाषित' है।

ngOnInit() {
  this.es.getEventList()
    .subscribe((response) => {
        this.myEvents = response;
    });

  console.log(this.myEvents); //Outside the subscribe block 'Undefined'
}

इसलिए यदि आप इसे सदस्यता ब्लॉक के अंदर लॉग इन करते हैं तो यह ठीक से प्रतिक्रिया लॉग करेगा।

ngOnInit(){
  this.es.getEventList()
    .subscribe((response)=>{
        this.myEvents = response;
        console.log(this.myEvents); //Inside the subscribe block 'http response'
    });
}

3

यहाँ समस्या है, तो आप आरंभ कर रहे हैं this.myEventsमें subscribe()जो एक अतुल्यकालिक ब्लॉक, जबकि आप कर रहे हैं console.log()बस से बाहर subscribe()ब्लॉक। इसलिए आरंभ करने console.log()से पहले बुलाया जा रहा this.myEventsहै।

कृपया अपने कंसोल.लॉग () कोड के साथ-साथ सदस्यता () के अंदर ले जाएँ और आप कर रहे हैं।

ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
            console.log(this.myEvents);
        });
  }

2

परिणाम अपरिभाषित है क्योंकि कोणीय प्रक्रिया async। आप नीचे के रूप में कोशिश कर सकते हैं:

async ngOnInit(){
    const res = await this.es.getEventList();
    console.log(JSON.stringify(res));
}

1

यह भी सुनिश्चित करें कि आप अपनी प्रतिक्रिया को json आउटपुट में मैप करते हैं। अन्यथा यह सादे पाठ को लौटा देगा। आप इसे इस तरह से करते हैं:

getEventList(): Observable<any> {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });

return this.http.get("http://localhost:9999/events/get", options)
            .map((res)=>{ return res.json();}) <!-- add call to json here
            .catch((err)=>{return err;})
}

1

अपरिभाषित क्योंकि सेवा के किसी भी डेटा से पहले यहां लॉग इन किया गया मान उस उपर्युक्त सेवा कॉल से निर्धारित किया गया है। इसलिए आपको तब तक इंतजार करना होगा जब तक कि अजाक्स कॉल खत्म न हो जाए और प्रतिक्रिया डेटा से डेटा सेट न हो जाए।

getEventList(): Observable<any>{
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });

    return this.http.get("http://localhost:9999/events/get", options)
                .map((res)=> res.json())
                .catch((err)=> err)
  }

यहाँ सदस्यता विधि के अंदर कंसोल लॉग करें जो डेटा को myEvents चर में सेट करने पर लॉग बना देगा।

ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
     // This prints the value from the response
    console.log(this.myEvents)
        }); 
  }

0

आप बस इस विधि को आजमा सकते हैं-

let headers = new Headers({'Accept': 'application/json'});
let options = new RequestOptions({headers: headers});

return this.http
    .get(this.yourSearchUrlHere, options) // the URL which you have defined
    .map((res) => {
        res.json(); // using return res.json() will throw error
    }
    .catch(err) => {
        console.error('error');
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.