map()以及后面要说到的flatMap()之类的方法,是函数式编程里面常用到的方法,意思就是将原先的数据集里面的每一条数据,经过一定的处理再返回一个新的结果,也就是把一个数据集转换成另一个数据集。
现在,我们的WikipediaSerice的返回结果就不是Promise了,所以我们就需要修改app.ts,我们不能再使用then()方法来处理结果,而是使用subscribe()添加一个消息订阅方法。
this.term.valueChanges .debounceTime(400) .distinctUntilChanged() .subscribe( term => this.wikipediaService.search(term).subscribe( items => this.items = items ) );
其中,第一个subscribe():
this.term.valueChanges...subscribe(term => ....)
这个是对输入框产生的查询字符串,注册一个订阅方法,来处理用户的输入。
第二个subscribe():
this.wikipediaService.search(term).subscribe(items => this.items = items));
是对从服务器端返回的数据查询结果,注册一个订阅方法,来将这个数据赋值到model上。
我们也可以用下面的方式,来避免这样使用多个subscribe:
this.term.valueChanges .debounceTime(400) .distinctUntilChanged() .flatMap(term => this.wikipediaService.search(term)) .subscribe(items => this.items = items);
我们在用户输入的字符串的Observable<string>上调用flatMap(...)方法,相当于,对用户输入的每个有效的查询条件,调用wikipediaService.search()方法。然后对这个查询返回的数据,再注册一个订阅方法。
费了这么大的篇幅,希望你明白了Observable的flatMap和subscribe用法,对于没有接触过函数式编程的人来说,这确实不好理解,但是在Angular2里面,我们将会大量使用各种函数式编程的方法。所以还是需要你花时间慢慢理解。
费了这么大功夫,上面说的似乎跟'忽略之前未及时返回的消息'好像没什么关系,那么上面的修改到底有没有解决那个问题呢。没有!确实是没有。因为我们使用flatMap,对用户输入的每个有效的查询字符串,都会调用订阅的那个处理函数,然后更新model。所以我们的问题还是没有解决。
但是到了这一步以后,解决办法就很容易了,我们只需要用switchMap代理flatMap就可以。就这么简单!这是因为,switchMap会在处理每一个新的消息的时候,就直接把上一个消息注册的订阅方法直接取消掉。
最后,再优化一下代码:
@Component({ selector: 'my-app', template: ` <div> <h2>Wikipedia Search</h2> <input type="text" [formControl]="term"/> <ul> <li *ngFor="let item of items | async">{{item}}</li> </ul> </div> ` }) export class AppComponent { items: Observable<Array<string>>; term = new FormControl(); constructor(private wikipediaService: WikipediaService) { this.items = this.term.valueChanges .debounceTime(400) .distinctUntilChanged() .switchMap(term => this.wikipediaService.search(term)); } }
我们直接把switchMap()的结果,赋给model对象this.items,也就是一个Observable<Array<string>>类型的数据。这样,在模板里面使用items的地方也需要修改,使用AsyncPipe就可以:
<li *ngFor="let item of items | async">{{item}}</li>
这样,模板在解析items这个model的时候,就会自动解析这个Observable的结果,再渲染页面。