的Promise让我们用一个具体的例子来说明。假设您正在构建一个搜索输入掩码,该掩码应在您键入时立即显示结果。如果您曾经建造过这样的东西,您可能会意识到这项任务带来的挑战。不要在每次击键时都点击搜索端点将搜索端点想象成您为每个请求付费。不管是不是你自己的硬件。我们不应该比我们需要的更频繁地访问搜索端点。基本上我们只希望在用户停止输入后点击它,而不是每次击键。不要在后续请求中使用相同的查询参数命中搜索端点假设您键入foo,停止,键入另一个o,然后立即退格并返回到foo。这应该只是一个带有foo一词的请求,而不是两个,即使我们在搜索框中出现foo后技术上停止了两次。3.处理乱序响应当我们同时处理多个请求时,我们必须考虑它们以意外顺序返回的情况。考虑我们先输入computer,stop,requestmade,我们输入car,stop,requestmade。现在我们有两个请求正在进行中。不幸的是,在请求为汽车携带结果之后,为计算机携带结果的请求又回来了。这可能是因为它们由不同的服务器提供服务。如果我们不正确处理这种情况,我们最终可能会显示计算机的结果,而搜索框显示的是汽车。我们将使用免费开放的WikipediaAPI编写一个小型演示。为简单起见,我们的演示将只包含两个文件:app.ts和wikipedia-service.ts。但是,在现实世界中,我们可能会进一步拆分。让我们从不处理任何所述边缘情况的基于Promise的实现开始。这就是我们的WikipediaService的样子。使用AngularHTTP服务jsonp:上图使用toPromise方法将jsonp从angular/http库返回的对象转换为promise。简而言之,我们正在注入Jsonp服务以使用给定的搜索词来定位wiki。WikipediaAPI发出GET请求。请注意,我们调用toPromise以从Observable\获取Promise\。通过then-chaining,我们最终得到一个Promise\\>作为我们搜索方法的返回类型。到目前为止一切顺利,让我们看看包含App组件的app.ts文件。看一下wiki服务是如何使用的:这里也没有什么奇怪的。我们注入我们的WikipediaService并通过搜索方法将其功能暴露给模板。模板简单地绑定到keyup并调用search(term.value)以利用Angular令人敬畏的模板引用功能。我们解包由WikipediaService的搜索方法返回的Promise结果,并将其作为一个简单的字符串数组公开给模板,这样我们就可以让*ngFor循环遍历它并为我们构建一个列表。不幸的是,这个实现没有解决我们想要处理的任何描述的边缘情况。让我们重构我们的代码,使其按预期运行。让我们更改我们的代码,这样我们就不会在每次击键时都点击端点,而是仅在用户停止输入400毫秒后才发送请求。这就是Observables真正闪耀的地方。ReactiveExtensions(Rx)提供了广泛的操作符,允许我们改变Observables的行为并创建具有所需语义的新Observables。要揭示这样的超能力,我们首先需要获得一个Observable\,其中包含用户输入的搜索词。我们可以利用Angular的formControl指令,而不是手动绑定到keyup事件。要使用这个指令,我们首先需要将ReactiveFormsModule导入到我们的应用程序模块中。导入后,我们可以使用模板中的formControl并将其设置为名称“term”。在我们的组件中,我们从@angular/form创建一个FormControl实例,并将其作为组件上名称term下的字段公开。在幕后,术语自动将Observable\公开为我们可以订阅的属性valueChanges。现在我们有了一个Observable\,驯服用户输入就像在我们的Observable上调用debounceTime(400)一样简单。这将返回一个新的Observable\,如果400毫秒内没有新值出现,它只会发出一个新值。在我们期望的时间间隔之后,这个新对象将有一个新值。至于如何控制时间间隔,对于前端开发者来说是一个黑盒子。导出类App{items:Array;term=newFormControl();构造函数(私人维基百科服务:WikipediaService){this.term.valueChanges.debounceTime(400).subscribe(term=>this.wikipediaService.search(term).then(items=>this.items=items));正如我们所说,如果我们的应用程序已经显示了搜索词的结果,那么再次请求搜索词将是一种资源浪费。幸运的是,Rx简化了许多几乎不需要提及的操作。要实现所需的行为,我们所要做的就是在调用debounceTime(400)之后立即调用distinctUntilChanged运算符。同样,我们将返回一个Observable\,但它会忽略与前一个相同的值。处理无序响应处理无序响应可能是一项棘手的任务。基本上,我们需要一种方法来表明一旦发出新请求,我们就不再对先前进行中的请求的结果感兴趣。换句话说:一旦我们开始一个新的请求,就取消所有以前的请求。正如我在开头简要提到的,Observables是一次性的,这意味着我们可以取消订阅它们。这是我们想要更改WikipediaService以返回Observable\>而不是Promise\>的地方。就像删除toPromise并使用map而不是then一样简单。搜索(术语:字符串){varsearch=newURLSearchParams()search.set('action','opensearch');search.set('搜索',术语);search.set('格式','json');returnthis.jsonp.get('http://en.wikipedia.org/w/api.php?callback=JSONP_CALLBACK',{search}).map((response)=>response.json()[1]);}现在我们的WikipediaService返回一个Observable而不是Promise,我们只需要在我们的App组件中将then替换为subscribe。this.term.valueChanges.debounceTime(400).distinctUntilChanged().subscribe(term=>this.wikipediaService.search(term).subscribe(items=>this.items=items));但是现在我们有两个订阅调用。这是不必要的冗长,通常是需要重构代码的标志。好消息是,既然搜索返回了一个Observable>,我们可以简单地使用flatMap通过组合Observable将Observable投影到所需的Observable>中。this.term.valueChanges.debounceTime(400).distinctUntilChanged().flatMap(term=>this.wikipediaService.search(term)).subscribe(items=>this.items=items);你可能想知道flatMap是做什么的,为什么我们不能在这里使用map。答案很简单。映射运算符接受一个函数,该函数接受一个值T并返回一个值U。例如,一个函数接受一个字符串并返回一个数字。因此,当您使用map时,您会从Observable\获得Observable\。但是,我们的搜索方法本身会生成一个Observable。因此,从我们的ObservableafterdistinctUntilChanged开始,map将把我们带到Observable>。这不是我们想要的。另一方面,flatMap运算符接受一个函数,该函数接受一个T并返回一个Observable\并为我们生成一个Observable\。注意:这并不完全正确,但有助于简化。这完全符合我们的情况。我们有一个Observable\,然后使用一个函数调用flatMap,该函数接受一个字符串并返回一个Observable\>。现在我们已经了解了语义,我们可以使用一个小技巧来节省一些输入。我们可以让Angular直接在模板中为我们解包,而不是手动订阅Observable。我们所要做的就是在我们的模板中使用AsyncPipe并公开Observable\>而不是Array\。@Component({selector:'my-app',template:``})exportclassApp{items:Observable>;term=newFormControl();构造函数(私人维基百科服务:维基百科服务){this.items=this.term.valueChanges.debounceTime(400).distinctUntilChanged().switchMap(term=>this.wikipediaService.search(term));}}更多Jerry的原创文章,尽在:"汪子熙":