Observable 包装

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

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

包装 ajax 调用

let stream = Rx.Observable.create((observer) => {
   let request = new XMLHttpRequest();

   request.open( ‘GET’, ‘url’ );
   request.onload =() =>{
      if(request.status === 200) {
         observer.next( request.response );
         observer.complete();
     } else {
          observer.error('error happened');
     }
   }

   request.onerror = () => {  
       observer.error('error happened')
   }
   request.send();
})

stream.subscribe(
   (data) => console.log( data )  
)

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

发出数据

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

}

处理潜在的错误

else {
   observer.error('error happened');
}

关闭流

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

包装语音音频 API

console.clear();


const { Observable } = Rx;

const speechRecognition$ = new Observable(observer => {
   const speech = new webkitSpeechRecognition();

   speech.onresult = (event) => {
     observer.next(event);
     observer.complete();
   };

   speech.start();

   return () => {
     speech.stop();
   }
});

const say = (text) => new Observable(observer => {
  const utterance = new SpeechSynthesisUtterance(text);
  utterance.onend = (e) => {
    observer.next(e);
    observer.complete();
  };
  speechSynthesis.speak(utterance);
});


const button = document.querySelector("button");

const heyClick$ = Observable.fromEvent(button, 'click');

heyClick$
  .switchMap(e => speechRecognition$)
  .map(e => e.results[0][0].transcript)
  .map(text => {
    switch (text) {
      case 'I want':
        return 'candy';
      case 'hi':
      case 'ice ice':
        return 'baby';
      case 'hello':
        return 'Is it me you are looking for';
      case 'make me a sandwich':
      case 'get me a sandwich':
        return 'do it yo damn self';
      case 'why are you being so sexist':
        return 'you made me that way';
      default:
        return `I don't understand: "${text}"`;
    }
  })
  .concatMap(say)
  .subscribe(e => console.log(e));

语音识别流

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

const speechRecognition$ = new Observable(observer => {
   const speech = new webkitSpeechRecognition();

   speech.onresult = (event) => {
     observer.next(event);
     observer.complete();
   };

   speech.start();

   return () => {
     speech.stop();
   }
});

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

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

return () => {
   speech.stop();
 }

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

语音合成, say

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

const say = (text) => new Observable(observer => {
  const utterance = new SpeechSynthesisUtterance(text);
  utterance.onend = (e) => {
    observer.next(e);
    observer.complete();
  };
  speechSynthesis.speak(utterance);
});

主体流 hey$

heyClick$
  .switchMap(e => speechRecognition$)
  .map(e => e.results[0][0].transcript)
  .map(text => {
    switch (text) {
      case 'I want':
        return 'candy';
      case 'hi':
      case 'ice ice':
        return 'baby';
      case 'hello':
        return 'Is it me you are looking for';
      case 'make me a sandwich':
      case 'get me a sandwich':
        return 'do it yo damn self';
      case 'why are you being so sexist':
        return 'you made me that way';
      default:
        return `I don't understand: "${text}"`;
    }
  })
  .concatMap(say)
  .subscribe(e => console.log(e));

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

这一切归功于 @ladyleet 和 @benlesh

总结

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

results matching ""

    No results matching ""