• Observable 包装
    • 包装 ajax 调用
      • 发出数据
      • 处理潜在的错误
      • 关闭流
    • 包装语音音频 API
      • 语音识别流
      • 语音合成, say
      • 主体流 hey$
    • 总结

    Observable 包装

    在 Observable 剖析章节中我们只学到了关键操作符 next()error()complete(),如果是我们自己定义的 Observable 的话,可以使用这些方法来驱动 Observable 。我们还学到了,这些方法会触发相应的回调函数。

    用 Observable 包装意味着接收一些非 Observable 的东西并将其转换为 Observable,这样就可以很好的与其它 Observable 配合使用。同样还意味着现在我们可以使用操作符了。

    包装 ajax 调用

    1. let stream = Rx.Observable.create((observer) => {
    2. let request = new XMLHttpRequest();
    3. request.open( GET’, url );
    4. request.onload =() =>{
    5. if(request.status === 200) {
    6. observer.next( request.response );
    7. observer.complete();
    8. } else {
    9. observer.error('error happened');
    10. }
    11. }
    12. request.onerror = () => {
    13. observer.error('error happened')
    14. }
    15. request.send();
    16. })
    17. stream.subscribe(
    18. (data) => console.log( data )
    19. )

    这里我们需要做的三件事:发出数据错误处理关闭流

    发出数据

    1. if(request.status === 200) {
    2. observer.next( request.response ) // 发出数据
    3. }

    处理潜在的错误

    1. else {
    2. observer.error('error happened');
    3. }

    关闭流

    1. if(request.status === 200) {
    2. observer.next( request.response )
    3. observer.complete() // 关闭流,因为我们不想要更多的数据了
    4. }

    包装语音音频 API

    1. console.clear();
    2. const { Observable } = Rx;
    3. const speechRecognition$ = new Observable(observer => {
    4. const speech = new webkitSpeechRecognition();
    5. speech.onresult = (event) => {
    6. observer.next(event);
    7. observer.complete();
    8. };
    9. speech.start();
    10. return () => {
    11. speech.stop();
    12. }
    13. });
    14. const say = (text) => new Observable(observer => {
    15. const utterance = new SpeechSynthesisUtterance(text);
    16. utterance.onend = (e) => {
    17. observer.next(e);
    18. observer.complete();
    19. };
    20. speechSynthesis.speak(utterance);
    21. });
    22. const button = document.querySelector("button");
    23. const heyClick$ = Observable.fromEvent(button, 'click');
    24. heyClick$
    25. .switchMap(e => speechRecognition$)
    26. .map(e => e.results[0][0].transcript)
    27. .map(text => {
    28. switch (text) {
    29. case 'I want':
    30. return 'candy';
    31. case 'hi':
    32. case 'ice ice':
    33. return 'baby';
    34. case 'hello':
    35. return 'Is it me you are looking for';
    36. case 'make me a sandwich':
    37. case 'get me a sandwich':
    38. return 'do it yo damn self';
    39. case 'why are you being so sexist':
    40. return 'you made me that way';
    41. default:
    42. return `I don't understand: "${text}"`;
    43. }
    44. })
    45. .concatMap(say)
    46. .subscribe(e => console.log(e));

    语音识别流

    这将激活浏览器的麦克风并记录我们的语音

    1. const speechRecognition$ = new Observable(observer => {
    2. const speech = new webkitSpeechRecognition();
    3. speech.onresult = (event) => {
    4. observer.next(event);
    5. observer.complete();
    6. };
    7. speech.start();
    8. return () => {
    9. speech.stop();
    10. }
    11. });

    这段代码建立了语音识别 API,然后等待响应,并在响应一次后完成流,很像第一个使用 AJAX 的示例。

    注意还定义一个函数用来清理

    1. return () => {
    2. speech.stop();
    3. }

    所以我们可以通过调用 speechRecognition.unsubscribe() 来清理系统资源

    语音合成, say

    函数 say 负责发出你想要表达的语音。

    1. const say = (text) => new Observable(observer => {
    2. const utterance = new SpeechSynthesisUtterance(text);
    3. utterance.onend = (e) => {
    4. observer.next(e);
    5. observer.complete();
    6. };
    7. speechSynthesis.speak(utterance);
    8. });

    主体流 hey$

    1. heyClick$
    2. .switchMap(e => speechRecognition$)
    3. .map(e => e.results[0][0].transcript)
    4. .map(text => {
    5. switch (text) {
    6. case 'I want':
    7. return 'candy';
    8. case 'hi':
    9. case 'ice ice':
    10. return 'baby';
    11. case 'hello':
    12. return 'Is it me you are looking for';
    13. case 'make me a sandwich':
    14. case 'get me a sandwich':
    15. return 'do it yo damn self';
    16. case 'why are you being so sexist':
    17. return 'you made me that way';
    18. default:
    19. return `I don't understand: "${text}"`;
    20. }
    21. })
    22. .concatMap(say)
    23. .subscribe(e => console.log(e));

    整体逻辑应该是这样的:点击按钮激活 heyClick$speechRecognition$ 监听我们说了什么并把结果发送给 heyClick$ 的转换逻辑,转换逻辑的结果将由 say Observable 发出声音。

    这一切归功于 @ladyleet 和 @benlesh

    总结

    这两个 Observable 包装示例其中一个是简单些的 Ajax,而另一个是有一点点高级的语音 API 。但原理都是相同的: 1)数据是通过调用 next() 来发送的 2)如果没有更多的数据要发送则调用 complete() 3)如果有需要的话,定义一个清理函数可以通过 unsubscribe() 来调用 4)在合适的地方通过调用 error() 来进行错误处理。(只在第一个示例中这样做了)