Làm cách nào để trả lại phản hồi từ lệnh gọi Observable / http / async ở dạng góc?


110

Tôi có dịch vụ trả về một dịch vụ có thể quan sát được thực hiện yêu cầu http đến máy chủ của tôi và lấy dữ liệu. Tôi muốn sử dụng dữ liệu này nhưng tôi luôn nhận được undefined. Vấn đề là gì?

Dịch vụ :

@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)
  }
}

Thành phần:

@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!
  }
}

Tôi đã kiểm tra Làm cách nào để trả lại phản hồi từ cuộc gọi không đồng bộ? đăng nhưng không tìm thấy giải pháp


2
đó sẽ là một điểm tốt để nhấn mạnh vào thực tế là không thể chuyển đổi một hoạt động không đồng bộ thành một hoạt động đồng bộ.
n00dl3

@ n00dl3 Cảm ơn vì mẹo! Tôi đã cố gắng giải thích trong phần "Điều bạn không nên làm:". Hãy chỉnh sửa nó.
eko


@ HereticMonkey rằng bài đăng đó đã được ghi có trong câu trả lời
eko

@eko Và? Điều đó có làm cho câu hỏi bớt trùng lặp không?
Heretic Monkey

Câu trả lời:


117

Lý do:

Lý do undefinedlà bạn đang thực hiện một hoạt động không đồng bộ. Có nghĩa là sẽ mất một khoảng thời gian để hoàn thành getEventListphương pháp (phụ thuộc chủ yếu vào tốc độ mạng của bạn).

Vì vậy, chúng ta hãy nhìn vào cuộc gọi http.

this.es.getEventList()

Sau khi bạn thực sự thực hiện ("kích hoạt") yêu cầu http, subscribebạn sẽ đợi phản hồi. Trong khi chờ đợi, javascript sẽ thực thi các dòng bên dưới mã này và nếu nó gặp phải các phép gán / hoạt động đồng bộ, nó sẽ thực thi chúng ngay lập tức.

Vì vậy, sau khi đăng ký getEventList()và chờ phản hồi,

console.log(this.myEvents);

dòng sẽ được thực hiện ngay lập tức. Và giá trị của nó là undefinedtrước khi phản hồi đến từ máy chủ (hoặc bất cứ thứ gì mà bạn đã khởi tạo nó ngay từ đầu).

Nó tương tự như làm:

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

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


Giải pháp:

Vậy chúng ta phải làm thế nào để khắc phục vấn đề này? Chúng tôi sẽ sử dụng hàm gọi lại là subscribephương thức. Bởi vì khi dữ liệu đến từ máy chủ, nó sẽ ở bên trong subscribecùng với phản hồi.

Vì vậy, thay đổi mã thành:

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

sẽ in phản hồi .. sau một thời gian.


Bạn nên làm gì:

Có thể có nhiều việc phải làm với câu trả lời của bạn ngoài việc chỉ ghi lại nó; bạn nên thực hiện tất cả các thao tác này bên trong lệnh gọi lại (bên trong subscribehàm), khi dữ liệu đến.

Một điều khác cần đề cập là nếu bạn đến từ một Promisenền tảng, thì lệnh thengọi lại tương ứng subscribevới có thể quan sát.


Những gì bạn không nên làm:

Bạn không nên cố gắng thay đổi hoạt động không đồng bộ thành hoạt động đồng bộ (không phải bạn có thể làm như vậy). Một trong những lý do mà chúng tôi có các hoạt động không đồng bộ là không bắt người dùng đợi một hoạt động hoàn thành trong khi họ có thể làm những việc khác trong khoảng thời gian đó. Giả sử rằng một trong các hoạt động không đồng bộ của bạn mất 3 phút để hoàn thành, nếu chúng tôi không có các hoạt động không đồng bộ, giao diện sẽ đóng băng trong 3 phút.


Cách đọc được đề nghị:

Tín dụng ban đầu cho câu trả lời này là: Làm cách nào để trả lại phản hồi từ một cuộc gọi không đồng bộ?

Nhưng với bản phát hành angle2, chúng ta đã được giới thiệu với các tệp có thể quan sát được và có thể quan sát được nên câu trả lời này hy vọng sẽ bao gồm các kiến ​​thức cơ bản về xử lý một yêu cầu không đồng bộ với các tệp quan sát được.


3
Khi một câu hỏi được trả lời bởi người hỏi tại cùng một thời điểm chính xác của bài .. Đây là một nơi tốt để dừng lại và viết một bài đăng blog thay vì
Jonas Praem

3
@JonasPraem Đúng, nhưng không có gì sai khi chia sẻ kiến ​​thức trên Stackoverflow. Như bạn thấy số phiếu bầu, nó đã giúp ích cho nhiều người ở đây và nó sẽ tiếp tục như vậy.
Amit Chigadani

11

Thực hiện cuộc gọi http trong angle / javascript là hoạt động không đồng bộ. Vì vậy, khi bạn thực hiện lệnh gọi http, nó sẽ gán luồng mới để kết thúc cuộc gọi này và bắt đầu thực hiện dòng tiếp theo với một luồng khác. Đó là lý do tại sao bạn đang nhận được giá trị không xác định. vì vậy hãy thực hiện thay đổi bên dưới để giải quyết vấn đề này

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

5

Bạn có thể sử dụng asyncPipe nếu bạn chỉ sử dụng myEvents trong mẫu.

Đây là ví dụ với asyncPipe và Angular4 HttpClient ví dụ


1
Và với một thực hiện điều này không cần phải ngừng đăng ký các Subscription (!): Medium.com/@sub.metu/...
San Jay Falcon

@SanJayFalcon Vì đây là một yêu cầu http nên không cần hủy đăng ký.
AJT82

3

Các khả năng quan sát là lười biếng, vì vậy bạn phải đăng ký để nhận giá trị. Bạn đã đăng ký nó đúng cách trong mã của mình nhưng đồng thời ghi đầu ra bên ngoài khối 'đăng ký'. Đó là lý do tại sao nó là 'không xác định'.

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

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

Vì vậy, nếu bạn đăng nhập nó bên trong khối đăng ký thì nó sẽ ghi lại phản hồi đúng cách.

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

3

Vấn đề ở đây là, bạn đang khởi tạo this.myEventsvào subscribe()đó là một khối không đồng bộ trong khi bạn đang thực hiện việc console.log()rời khỏi subscribe()khối. Vì vậy, console.log()được gọi trước khi this.myEventsđược khởi tạo.

Vui lòng di chuyển mã console.log () của bạn cũng như bên trong subscribe () và bạn đã hoàn tất.

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

2

Kết quả là không xác định vì quá trình góc không đồng bộ. bạn có thể thử như dưới đây:

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

1

Ngoài ra, hãy đảm bảo rằng bạn lập bản đồ phản hồi của mình với đầu ra json. Nếu không, nó sẽ trả về văn bản thuần túy. Bạn làm điều đó như thế này:

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

Không xác định vì giá trị ở đây được ghi lại trước khi bất kỳ dữ liệu nào từ dịch vụ được đặt từ cuộc gọi đăng ký dịch vụ ở trên. Vì vậy, bạn phải đợi cho đến khi cuộc gọi ajax kết thúc và thiết lập dữ liệu từ dữ liệu phản hồi.

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

Ở đây, hãy tạo nhật ký Console bên trong phương thức đăng ký sẽ tạo nhật ký khi dữ liệu được đặt trong biến myEvents.

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

0

Bạn chỉ cần thử phương pháp này-

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');
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.