学习Rxjs是两个月前的事了,但没有用到在一个实际需求上。近日收到一个需求,正好是一个可以抽象为由数据流驱动的应用,于是我欣然用Rxjs结合React写之。

需求说明

需求原始说明为:

60个英文单词被分为三个组系,每个组系包括2-6个单词一组的五组单词。在测试中,测试单词以1词/秒的速率依次呈现在电脑屏幕上,被试大声朗读屏幕上的单词,当一组单词呈现结束时,屏幕上出现与该组数目相等的问号,提示被试用刚才呈现的单词造句。 两个一组时,屏幕上以1词/秒的速率依次出现“price”,“week”,两个单词后会出现??,受试用price造句,造完句后点下鼠标,屏幕上出现一个问号,受试用week造句,之后点鼠标进入三个一组,屏幕上以1词/秒的速率依次出现“bird”,“game”,“ star”,三个单词后会出现???,受试用bird造句,造完句后点下鼠标, 屏幕上出现两个问号,受试用game造句,再点下鼠标, 屏幕上出现一个问号,受试用star造句,之后点鼠标进入四个一组…以此类推。

全程录音,计算机记录受试每个单词造句所用时间。

需求原始说明可能有些不易看明,经过讨论,我归总如下:

  1. 数据格式为:(word.组系序号.组单词数.组内序号)

    Test1 Test2 Test3
    word.1.2.1 word.2.2.1 word.3.2.1
    word.1.2.2 word.2.2.2 word.3.2.2
    word.1.3.1 word.2.3.1 word.3.3.1
    word.1.3.2 word.2.3.2 word.3.3.2
    word.1.3.3 word.2.3.3 word.3.3.3
    ... ... ...
    word.1.6.6 word.2.6.6 word.3.6.6

    由此,抽象为以下数据类型:

    // 代表一个组系
    interface TestItem {name: string; // 组系名,如:Test1, Test2, Test3/*二维数组表示的组系内数据项[word.1.2.1, word.1.2.2][word.1.3.1, word.1.3.2, word.1.3.3]...*/wordGroups: string[][];
    }
    复制代码

    并将数据放入全局store:

    class Store {// 组系items: TestItem[] = [];// 每组最小单词数minWordsCount = 2;// 每组最大单词数maxWordsCount = 6;// 实验开始前倒计时秒数countdown = 5;
    }
    复制代码
  2. 流程:

    1. 倒计时

    2. 对每个组系:

      1. 对每一组:

        1. 依次显示单词,每个单词显示一秒
        2. 对每个单词:
          1. 显示此组单词数 - i个问号
          2. 如果点击事件发生,子流程结束
        3. 子流程结束
      2. 子流程结束

    3. 流程结束

读者有意,可以试想一下普通做法如何实现。

数据流抽象

为了方便一些操作,我封装了一个immediateInterval,因为interval不是即时开始发射数据的:

function immediateInterval(period?: number | undefined,scheduler?: SchedulerLike | undefined
): Observable<number> {return new Observable(subscriber => {subscriber.next(0);const interval$ = interval(period, scheduler);const next = subscriber.next; subscriber.next = function(this: Subscriber<number>, i: number) {next.bind(this)(i + 1);}.bind(subscriber);interval$.subscribe(subscriber);});
}
复制代码

由上节的流程,得到如下数据流:

  1. 倒计时:使用immediateInterval发出store.countdown个值,每个值携带当前倒计时数,然后

  2. 迭代item of store.items

    1. 迭代group of item.wordGroups:

      1. 显示单词:使用immediateInterval发出group.length个值,每个值携带当前单词,然后

      2. 迭代for(let i = 0; i < group.length; i++)

        1. 发出值,携带group.length - i(问号数)
        2. 订阅全局event busclick事件,当click激发时,continue此迭代

数据流实现

以下代码的一些设计点:

  • 使用event busemit实现流程钩子,用来向外报告进行到了哪一步
  • 使用async await语法实现异步迭代:
    async () => {for(...){await new Promise(async (resolve) => {someAsyncCode(// 结束此Promise,以continue该for循环resolve(); )})}
    }
    复制代码
// event bus
const bus = new EventEmitter();const stream$ = new Observable<| { type: "倒计时"; count: number }| {type: "显示单词";word: string;}| {type: "显示问号";count: number;}
>(subscriber => {immediateInterval(1000).pipe(take(store.countdown + 1)).subscribe(i => {if (i !== store.countdown)subscriber.next({ type: "倒计时", count: store.countdown - i });},console.error,async () => {bus.emit("start", { type: "full" }); // 全程开始for (let item of store.items) {await new Promise(async (resolve, reject) => {bus.emit("start", { type: "item", item }); // 一组系开始for (let groupIndex = 0;groupIndex < item.wordGroups.length;groupIndex++) {const group = item.wordGroups[groupIndex];// 一组开始bus.emit("start", { type: "group", item, groupIndex }); // 一组开始await new Promise(async (resolve, reject) => {// 显示单词immediateInterval(1000).pipe(take(group.length)).subscribe(i => {subscriber.next({ type: "显示单词", word: group[i] });},console.error,() => {// 显示问号rxjs.timer(1000).subscribe(async () => {for (let i = group.length, wordIndex = 0;i > 0;i--, wordIndex++) {// 开始造句bus.emit("start", {type: "sentence",index: wordIndex,item,groupIndex});await new Promise(async (resolve, reject) => {subscriber.next({ type: "显示问号", count: i });const onClick = () => {resolve(); // next 问号bus.removeListener("click", onClick);};bus.addListener("click", onClick);});// 造句结束bus.emit("end", {type: "sentence",index: wordIndex,item,groupIndex});}resolve(); // next group});});});bus.emit("end", { type: "group", item, groupIndex }); // 一组结束}bus.emit("end", { type: "item", item }); // 一组系结束resolve(); // next test});}bus.emit("end", { type: "full" }); // 全程结束subscriber.complete();});
});
复制代码

连接到React组件(Hooks):

const Play = () => {const action = useObservable(() => stream$);let comp = null;if (!action) {comp = null;} else if (action.type === "倒计时") {comp = ...;} else if (action.type === "显示单词") {comp = ...;} else if (action.type === "显示问号") {comp = ...;}return ...;
};
复制代码

总结

Rxjs使得一些事情更容易(:

涉及到的一些库

rxjs

rxjs-hooks(rxjs与react hooks的结合库)

wolfy87-eventemitter(浏览器端的高效的EventEmitter)

转载于:https://juejin.im/post/5c54f00ef265da2de165761b

Rxjs初体验:制作语音测试工具相关推荐

  1. 体验Vs2005 beta2 测试工具

    在Vs2005中加入了单元测试工具,使用与NUnit差不多.但功能更加丰富了,而且使用更加方便,有利于项目的协调工作.而且还支持调试测试,(不知道NUnit支不支持,我是没用过.)方便我们调试出错代码 ...

  2. memsql 落地mysql_MemSQL初体验 - (2)初始化测试环境

    2.配置测试环境 创建一个用户,方便后续使用: MemSQL> grant all on *.* to jss identified by "jss"  with grant ...

  3. MemSQL初体验 - (2)初始化测试环境

    2.配置测试环境 创建一个用户,方便后续使用: MemSQL> grant all on *.* to jss identified by "jss"  with grant ...

  4. NSIS 之初体验 制作打印机i5100windows安装程序

    1.简介 NSIS(Nullsoft Scriptable Install System)是一个开源的 Windows 系统下安装程序制作程序.它提供了安装.卸载.系统设置.文件解压缩等功能.这如其名 ...

  5. CentOS 初体验六:登录工具PuTTY使用

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/78961584 本文出自[赵彦军的博客] PuTTY 简介 PuTTY是一个Teln ...

  6. 【第二趴】uni-app开发工具(手把手带你安装HBuilderX、搭建第一个多端项目初体验)

    文章目录 写在前面 HBuilderX HBuilderX 优势 HBuilderX 安装 uni-app 初体验 写在最后 写在前面 聚沙成塔--每天进步一点点,大家好我是几何心凉,不难发现越来越多 ...

  7. FlashCache初体验

    FlashCache初体验 注意: 测试用的是CentOS6.5 内核版本2.6.32-431.el6.x86_64 步骤: 上传CentOS6.5做本地yum源,安装以下包. yum install ...

  8. 【聆思CSK6视觉AI开发套件试用】CSK6系列头肩识别初体验

    本篇文章来自极术社区与聆思科技组织的CSK6 视觉AI开发套件活动,更多开发板试用活动请关注极术社区网站.作者:張弩拔劍 背景 前言 聆思CSK4002以先进的AI算法, 出色的性价比, 以及优越的头 ...

  9. 腾讯内部测试软件,腾讯性能测试工具——PerfDog使用初体验

    官网地址:https://perfdog.qq.com/ 使用说明:https://perfdog.qq.com/support 测试机型:锤子坚果pro2s (前几天得知我浩哥上了老赖名单,现在看着 ...

  10. OCR 工具tesseract初体验

    OCR 工具tesseract初体验 @(工具使用)[工具使用, python] OCR即图片上文字识别 安装tesseract github地址 tesseract是一个命令行程序,后面安装的pyt ...

最新文章

  1. Java引用计数与实现
  2. 通过QQ或者QQ帮助别人学习Lync汇总
  3. [Android]关于Root
  4. boost::lambda模块实现boost::function 进行测试
  5. node.js中的框架
  6. php 汉王云名片_汉王云-名片识别【最新版】_OCR_人工智能_API-云市场-阿里云
  7. 什么?你项目还在用Date表示时间?!
  8. 马斯克扎心了!猎鹰重型火箭核心助推器运输过程中坠海
  9. linux输入法_惊奇软件:这是我见过最有【态度】的输入法!
  10. 微信公众平台简易设计使用
  11. Springboot就业招聘信息系统x8y1g计算机毕业设计-课程设计-期末作业-毕设程序代做
  12. 数据库管理工具 FreeSQL
  13. 从谷歌创始人的公开信中所学到的
  14. 应用matlab仿真几类混沌电路,应用MATLAB仿真几类混沌电路
  15. 剑网三修复选择服务器,老玩家回坑剑网三去哪个区服,要注意啥?这有一份终极宝典请查收...
  16. AV1编码标准整体概述
  17. 去除小数点后的.或多余的0(多个方法参考)
  18. ap计算机科学a 巴郎 翻译,AP计算机科学a词汇整理 不知道怎么用暑假预习的你快看过来...
  19. 华为od机试真题 JS 实现【热点网站统计】
  20. 万字详解SSH(SSH登录原理+SSH配置+模拟实现SSH免密登录)

热门文章

  1. TV TimeShift和PVR的区别
  2. js_开发小技巧记录(一)
  3. Delphi / Pascal 语法知识干货
  4. 单词的理解 —— 通过上下文环境
  5. oracle 自增加列的实现
  6. OBJECT_ID(Transact-Sql)
  7. Java7 和 C# 2.0
  8. 我来做百科(第八天)
  9. Xcode Missing Private key
  10. 面试题1,值传递和参数传递