Angular的Observable可观察对象(转)
<p>原文:https://blog.csdn.net/qq_34414916/article/details/85194098</p><p>Observable</p>
<p>在开始讲服务之前,我们先来看一下一个新东西——Observable(可观察对象),是属于RxJS库里面的一个对象,可以用来处理异步事件,例如HTTP请求(实际上,在Angular中,所有的HTTP请求返回的都是Observable),或许,你以前接触过一个叫promise的东西,它们本质上面是相同的:都是生产者主动向消费者“push”产品,而消费者是被动接收的,但是他们两者还是有很大区别的:Observable可以发送任意多值,并且,在被订阅之前,它是不会执行的!这是promise不具备的特点,下面砸门来详细了解一下Observable</p>
<p>心法篇<br>Observable用于在发送方和接收方之间传输消息,为了更好地理解,你可以将这些消息看成是流<br>在创建Observable对象时,需要传入一个函数作为构造函数的参数,这个函数叫订阅者函数,这个函数也就是生产者向消费者推送消息的地方<br>在被消费者subscribe(订阅)之前,订阅者函数不会被执行,直到subscribe()函数被调用,该函数返回一个subscription对象,里面有一个unsubscribe()函数,消费者可以随时拒绝消息的接收!<br>subscribe()函数接收一个observer(观察者)对象作为入参<br>消息的发送可以是同步的,也可以是异步的<br>我们可以使用一系列的RxJS操作符,在这些消息被接收方接收之前,对它们进行一系列的处理、转换,因为这些操作符都是纯函数,没有副作用,可以放心使用,并不会产生期望之外的结果!<br>详细教程篇<br>1、observer(观察者)</p>
<p>有了可观察对象(发送方),怎么少得了observer(观察者)来观察可观察对象呢!observer是一个对象,其中包含三个属性:next,error,complete,它们都是函数</p>
<p>next:以接收的值作为入参,在正常情况下执行,可选<br>error:出错的情况下执行,可选<br>complete:传输完成的情况下执行,可选<br>当然,这都是由你自己代码决定的,(请继续往下阅读你就会明白)</p>
<p>2、初识Observable</p>
<p>我们先通过RxJS库中的(of)方法来创建一个Observable,而不是通过构造函数来创建(下一小节的内容)</p>
<p>在vscode中新建项目(文件夹),取名testRxJS,在该目录下新建一个.ts文件,取名teaching1.0.ts,内容如下</p>
<div class="cnblogs_code">
<pre>import { of } from "rxjs"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> f1() {
let observable </span>= of(1, 2, 3<span style="color: rgba(0, 0, 0, 1)">);
observable.subscribe({ next: num </span>=><span style="color: rgba(0, 0, 0, 1)"> console.log(num) });
}
f1();</span></pre>
</div>
<p>切换到vscode的命令行(ctrl+~),cd到本项目所在的文件夹,输入tsc .\teaching1.0.ts,然后输入node .\teaching.1.0.js,可以看到控制台有以下输出</p>
<p> <img src="https://img2018.cnblogs.com/blog/564326/201905/564326-20190515181443763-1908364015.png" alt=""></p>
<p>代码解析</p>
<p> (注意,如果上面关于怎样运行ts程序,或者vscode操作你不熟悉的话,请先阅读vscode中使用TypeScript,以及vscode一些常用的快捷键,特别是箭头函数,如果你不熟悉的话,后面的内容将无法进行!)</p>
<p>RxJS中的of方法用于创建一个Observable,它会将其参数一个一个的发送给接收方,正如这里所看到的,它将1,2,3分别发送<br>subscribe()函数中接受一个observer对象,但这里,只定义了next方法,可以发现next方法接受一个参数,而这个参数就是生产者发送过来的值!然后将其打印在控制台上,(至于为什么这个值就是生产者发送的值,请继续阅读,其实说白了,是由你代码决定的,在下一小节,我们将会使用Observable的构造函数来定义一个Observable,并自己定义订阅者函数)<br>3、订阅者函数</p>
<p>上面说了很多这样的句子:请继续阅读,那么,在这一小节,我将揭晓谜底</p>
<p>同目录下新建一个teaching1.1.ts文件(注意,这篇博客牵涉的所有代码都在该目录(testRxJS)下,后面就不多讲了),内容如下</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> f2() {
const observable </span>= Observable.create(observer =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
observer.next(</span>1<span style="color: rgba(0, 0, 0, 1)">);
observer.next(</span>2<span style="color: rgba(0, 0, 0, 1)">);
observer.next(</span>3<span style="color: rgba(0, 0, 0, 1)">);
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (e) {
observer.error(e);
}
observer.complete();
});
const observer </span>=<span style="color: rgba(0, 0, 0, 1)"> {
next: num </span>=><span style="color: rgba(0, 0, 0, 1)"> console.log(num),
error: e </span>=><span style="color: rgba(0, 0, 0, 1)"> console.log(e),
complete: () </span>=> console.log('complete!!!'<span style="color: rgba(0, 0, 0, 1)">)
}
observable.subscribe(observer);
}
f2();</span></pre>
</div>
<p> </p>
<p>用上面同样的方式运行,结果如下</p>
<p><img src="https://img2018.cnblogs.com/blog/564326/201905/564326-20190515181557233-1776229294.png" alt=""></p>
<p>可以发现,这运行效果同用of创建的Observable是一样的,只不过这里我还为observer定义了complete方法,所以它多输出了“complete!!!”。或许你也猜到了,这段代码就是of的内幕!!(注意,肯定不是of的源代码啊!),而且,如果出了错,就会被catch到,从而执行observer的error方法,通过这一小节,是不是对Observable有了更清晰的认识——原来都是有你的代码决定的,没有想象中的那么神秘</p>
<p>4、subscribe()(订阅)</p>
<p>在被消费者subscribe(订阅)之前,订阅者函数不会被执行,直到subscribe()函数被调用,该函数返回一个subscription对象,里面有一个unsubscribe()函数,消费者可以随时拒绝消息的接收!也就是说:在Observable调用subscribe函数之前,什么也不会发生,就像下面这段代码,控制台什么输出内容都没有</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> f1() {
let observable </span>= of(1, 2, 3<span style="color: rgba(0, 0, 0, 1)">);
}
f1();</span></pre>
</div>
<p>直到你订阅这个observable对象,像下面这样</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> f1() {
let observable </span>= of(1, 2, 3<span style="color: rgba(0, 0, 0, 1)">);
observable.subscribe({ next: num </span>=><span style="color: rgba(0, 0, 0, 1)"> console.log(num) });
}
f1();</span></pre>
</div>
<p> </p>
<p>5、异步发送消息</p>
<p>其实,可以发现,上面一下节生产者发送消息的方式是同步的!这一小节,我们来个异步发送消息(等待2秒,再向消费者发送数字4),代码如下</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> f2() {
const observable </span>= Observable.create(observer =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
let time </span>= 0<span style="color: rgba(0, 0, 0, 1)">;
observer.next(</span>1<span style="color: rgba(0, 0, 0, 1)">);
observer.next(</span>2<span style="color: rgba(0, 0, 0, 1)">);
observer.next(</span>3<span style="color: rgba(0, 0, 0, 1)">);
const intervalId </span>= setInterval(() =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(`wait ${</span>++<span style="color: rgba(0, 0, 0, 1)">time}s`);
}, </span>900<span style="color: rgba(0, 0, 0, 1)">)
setTimeout(() </span>=> { observer.next(4); clearInterval(intervalId) }, 2000<span style="color: rgba(0, 0, 0, 1)">);
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (e) {
observer.error(e);
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> observer.complete(); // 注意不能立即调用complete函数,不然会终止消息传输</span>
setTimeout(() => observer.complete(), 3000<span style="color: rgba(0, 0, 0, 1)">)
});
const observer </span>=<span style="color: rgba(0, 0, 0, 1)"> {
next: num </span>=><span style="color: rgba(0, 0, 0, 1)"> console.log(num),
error: e </span>=><span style="color: rgba(0, 0, 0, 1)"> console.log(e),
complete: () </span>=> console.log('complete!!!'<span style="color: rgba(0, 0, 0, 1)">)
}
observable.subscribe(observer);
}
f2();</span></pre>
</div>
<p> </p>
<p>运行结果如下</p>
<p><img src="https://img2018.cnblogs.com/blog/564326/201905/564326-20190515181939548-270611574.png" alt=""> </p>
<p>代码解析</p>
<p>这里的代码可能有点复杂,但也是有基本元素组成:定时器、箭头函数,只要了解这两个知识点,这段代码应该就没有难度,但需要注意下面几点</p>
<p>complete不能立即调用,因为数字4的传输实在2秒后,如果你立即调用complete函数,就会中断传输,数字4也就接收不到了<br>注意到setInterval中的时间间隔并不是1000ms,我把它写成了900ms,可能是一些微小的时间延迟的原因,写成1000ms并不会产生期望的结果<br>最后产生的结果就是:数字4被异步发送了!!!<br>5、初识unsubscribe()函数</p>
<p>在这一小节,我们直接调用unsubscribe()函数,只为看看效果,下一小节,我们将自己写一个subscribe函数,并返回一个包含unsubscribe函数的对象,用于中断信息传输</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> f3() {
const obs </span>= Observable.create(observer =><span style="color: rgba(0, 0, 0, 1)"> {
observer.next(</span>1<span style="color: rgba(0, 0, 0, 1)">);
setTimeout(() </span>=> observer.next(2), 2000); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 等待两秒才发送下一个值</span>
<span style="color: rgba(0, 0, 0, 1)"> });
let suber </span>=<span style="color: rgba(0, 0, 0, 1)"> obs.subscribe({
next: x </span>=> console.log("接收到:"<span style="color: rgba(0, 0, 0, 1)">, x),
});
setTimeout(() </span>=> suber.unsubscribe(), 1000); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在一秒后取消订阅</span>
<span style="color: rgba(0, 0, 0, 1)">}
f3();</span></pre>
</div>
<p> </p>
<p>运行结果如下</p>
<p> <img src="https://img2018.cnblogs.com/blog/564326/201905/564326-20190515182047044-1181002023.png" alt=""></p>
<p>可以发现,接收方只接收到了数字1,</p>
<p>代码解析</p>
<p>因为数字2 在两秒钟后才会被发送,而消费者在1秒钟后便中断了消息传输,所以,控制台上面只打印了数字1<br>6、自定义subscribe()</p>
<p>看到这里,我想你应该也有些想法了,前面说过,Observable的subscribe函数返回一个subscription对象,其中有一个unsubscribe函数,用于取消订阅,你应该对subscribe函数的内部实现有着很多想法,现在,我们试着自己写一个!(在这里,我们就不需要Observable对象了,我们使用函数调用来模拟订阅),代码如下</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> f4() {
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> subscribe(observer) {
</span><span style="color: rgba(0, 0, 255, 1)">var</span> intervalID = setInterval(() =><span style="color: rgba(0, 0, 0, 1)"> {
observer.next(</span>'launch.....'<span style="color: rgba(0, 0, 0, 1)">);
}, </span>1000<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> {
unsubscribe: () </span>=><span style="color: rgba(0, 0, 0, 1)"> clearInterval(intervalID)
}
}
</span><span style="color: rgba(0, 0, 255, 1)">var</span> subscription = subscribe({ next: (x) =><span style="color: rgba(0, 0, 0, 1)"> console.log(x) });
setTimeout(() </span>=> subscription.unsubscribe(), 5000<span style="color: rgba(0, 0, 0, 1)">);
}
f4();</span></pre>
</div>
<p> </p>
<p>运行结果如下</p>
<p><img src="https://img2018.cnblogs.com/blog/564326/201905/564326-20190515182232502-1866095215.png" alt=""> </p>
<p>代码解析</p>
<p>这段代码的意思是:每隔1秒向控制台打印“launch....”,在第5秒的时候,取消打印(模拟取消订阅的过程)<br>可以发现,这个subscribe函数返回了一个对象!该对象中有一个unsubscribe函数!<br>通过这段代码,我们就模拟了Observable的subscribe函数的内部实现(仅仅是模拟而已)<br>7、使用RxJS中的操作符</p>
<p>在心法篇的时候,我就讲过这些操作符的重要作用以及优点,实际上,在开发实际应用的时候,你离不开它,在这里,我只举几个简单的例子,大概了解一下他们的用法就好</p>
<p>使用单个操作符<br>在这里,以map这个操作符为例,它可以接收可观察对象发送的值,并将其转换另外的形式,并以一个可观察对象发送这些新值,现在,我们来写一段有趣的代码:将原来的可观察对象发送的值全部转换为“hello world”,并订阅这个操作符返回的新的可观察对象,并输出值,代码如下</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> f5() {
const observable </span>= of(1, 2, 3<span style="color: rgba(0, 0, 0, 1)">);
const opt </span>= map(num => 'hello world'<span style="color: rgba(0, 0, 0, 1)">);
const newObservable </span>=<span style="color: rgba(0, 0, 0, 1)"> opt(observable);
newObservable.subscribe(data </span>=><span style="color: rgba(0, 0, 0, 1)"> console.log(data));
}
f5();</span></pre>
</div>
<p>运行结果如下</p>
<p> <img src="https://img2018.cnblogs.com/blog/564326/201905/564326-20190515182315025-91391537.png" alt=""></p>
<p>可以发现map将1,2,3转换成了三个hello world并输出在控制台上,你也可以发现,这种方式代码结构并不是很清晰(特别是在有多个操作符的情况下),实际上,并不推荐这种写法,下面我们来看看另外一种更好的写法</p>
<p>使用多个操作符<br>我们可以使用管道(pipe),将多个操作符链接起来,并将操作符返回的结果组合成一个,这样,代码结构更清晰,在这里,我将把map、tap这两个操作符链接起来,使得,你既可以看到原可观察对象发送的值,你也可以看到转换后的结果</p>
<p>tip:tap操作符,可以理解为窥探,就是“不打扰”可观察对象发送的值,但是又可以取得这些值进行处理,常见的用法就是——调试功能</p>
<p>function f6() {<br> const observable = of(1, 2, 3);<br> const newObservable = observable.pipe(<br> tap(num => console.log(num)),<br> map(num => 'hello world')<br> );<br> newObservable.subscribe(data => console.log(data));<br>}<br>f6();<br>运行结果如下</p>
<p> </p>
<p>这些写代码是不是很清晰呀!</p>
<p><br>--------------------- <br>作者:数星星等天明 <br>来源:CSDN <br>原文:https://blog.csdn.net/qq_34414916/article/details/85194098 <br>版权声明:本文为博主原创文章,转载请附上博文链接!</p><br><br>
来源:https://www.cnblogs.com/ajianbeyourself/p/10871215.html 哇,感谢楼主的分享!这篇文章讲得很详细啊,让我对Observable有了更清晰的认识。
之前一直对Promise和Observable的区别有点模糊,现在终于搞清楚了——Observable可以发送多个值,而且是被订阅了才会执行,这点确实很强大。
学到的几个关键点:
[*]Observable是懒加载的,不订阅就不会执行
[*]Observer的三个方法:next、error、complete
[*]unsubscribe()可以随时取消订阅,这个在做组件销毁时很有用
[*]pipe和操作符的组合使用让代码更清晰
不过有个小建议:文章里用的Observable.create在RxJS 6+版本中已经deprecated了,现在更推荐使用new Observable()或者from、of这类创建操作符。当然,核心原理还是一样的。
另外,Angular中HTTP请求返回的就是Observable这点真的很实用,配合操作符链式调用比Promise.then优雅很多。
RxJS官方文档也有很详细的内容,有兴趣的可以去看看。
总之感谢楼主的整理,收藏了!👍
頁:
[1]