构建流式应用—RxJS详解

2023-11-20

       最近在 Alloyteam Conf 2016 分享了《使用RxJS构建流式前端应用》,会后在线上线下跟大家交流时发现对于 RxJS 的态度呈现出两大类:有用过的都表达了 RxJS 带来的优雅编码体验,未用过的则反馈太难入门。所以,这里将结合自己对 RxJS 理解,通过 RxJS 的实现原理、基础实现及实例来一步步分析,提供 RxJS 较为全面的指引,感受下使用 RxJS 编码是怎样的体验。

目录

  • 常规方式实现搜索功能
  • RxJS · 流 Stream
  • RxJS 实现原理简析
    • 观察者模式
    • 迭代器模式
    • RxJS 的观察者 + 迭代器模式
  • RxJS 基础实现
    • Observable
    • Observer
  • RxJS · Operators
    • Operators ·入门
    • 一系列的 Operators 操作
  • 使用 RxJS 一步步实现搜索功能
  • 总结

常规方式实现搜索

做一个搜索功能在前端开发中其实并不陌生,一般的实现方式是:监听文本框的输入事件,将输入内容发送到后台,最终将后台返回的数据进行处理并展示成搜索结果。

<input id="text"></input>
<script>
    var text = document.querySelector('#text');
    text.addEventListener('keyup', (e) =>{
        var searchText = e.target.value;
        // 发送输入内容到后台
        $.ajax({
            url: `search.qq.com/${searchText}`,
            success: data => {
              // 拿到后台返回数据,并展示搜索结果
              render(data);
            }
        });
    });
</script>

上面代码实现我们要的功能,但存在两个较大的问题:

  1. 多余的请求
    当想搜索“爱迪生”时,输入框可能会存在三种情况,“爱”、“爱迪”、“爱迪生”。而这三种情况将会发起 3 次请求,存在 2 次多余的请求。

  2. 已无用的请求仍然执行
    一开始搜了“爱迪生”,然后马上改搜索“达尔文”。结果后台返回了“爱迪生”的搜索结果,执行渲染逻辑后结果框展示了“爱迪生”的结果,而不是当前正在搜索的“达尔文”,这是不正确的。

减少多余请求数,可以用 setTimeout 函数节流的方式来处理,核心代码如下

<input id="text"></input>
<script>
    var text = document.querySelector('#text'),
        timer = null;
    text.addEventListener('keyup', (e) =>{
        // 在 250 毫秒内进行其他输入,则清除上一个定时器
        clearTimeout(timer);
        // 定时器,在 250 毫秒后触发
        timer = setTimeout(() => {
            console.log('发起请求..');
        },250)
    })
</script>

已无用的请求仍然执行的解决方式,可以在发起请求前声明一个当前搜索的状态变量,后台将搜索的内容及结果一起返回,前端判断返回数据与当前搜索是否一致,一致才走到渲染逻辑。最终代码为

<input id="text"></input>
<script>
    var text = document.querySelector('#text'),
        timer = null,
        currentSearch = '';

    text.addEventListener('keyup', (e) =>{
        clearTimeout(timer)
        timer = setTimeout(() => {
            // 声明一个当前所搜的状态变量
            currentSearch = ''; 

            var searchText = e.target.value;
            $.ajax({
                url: `search.qq.com/${searchText}`,
                success: data => {
                    // 判断后台返回的标志与我们存的当前搜索变量是否一致
                    if (data.search === currentSearch) {
                        // 渲染展示
                        render(data);
                    } else {
                        // ..
                    }
                }           
            });
        },250)
    })
</script>

上面代码基本满足需求,但代码开始显得乱糟糟。我们来使用 RxJS 实现上面代码功能,如下

var text = document.querySelector('#text');
var inputStream = Rx.Observable.fromEvent(text, 'keyup')
                    .debounce(250)
                    .pluck('target', 'value')
                    .flatMapLatest(url => Http.get(url))
                    .subscribe(data => render(data));

可以明显看出,基于 RxJS 的实现,代码十分简洁!

RxJS · 流 Stream

RxJS 是 Reactive Extensions for JavaScript 的缩写,起源于 Reactive Extensions,是一个基于可观测数据流在异步编程应用中的库。RxJS 是 Reactive Extensions 在 JavaScript 上的实现,而其他语言也有相应的实现,如 RxJava、RxAndroid、RxSwift 等。学习 RxJS,我们需要从可观测数据流(Streams)说起,它是 Rx 中一个重要的数据类型。

是在时间流逝的过程中产生的一系列事件。它具有时间与事件响应的概念。

rxjs_stream

下雨天时,雨滴随时间推移逐渐产生,下落时对水面产生了水波纹的影响,这跟 Rx 中的流是很类似的。而在 Web 中,雨滴可能就是一系列的鼠标点击、键盘点击产生的事件或数据集合等等。

RxJS 基础实现原理简析

对流的概念有一定理解后,我们来讲讲 RxJS 是怎么围绕着流的概念来实现的,讲讲 RxJS 的基础实现原理。RxJS 是基于观察者模式和迭代器模式以函数式编程思维来实现的。

观察者模式

观察者模式在 Web 中最常见的应该是 DOM 事件的监听和触发。

  • 订阅:通过 addEventListener 订阅 document.body 的 click 事件。
  • 发布:当 body 节点被点击时,body 节点便会向订阅者发布这个消息。
document.body.addEventListener('click', function listener(e) {
    console.log(e);
},false);

document.body.click(); // 模拟用户点击

将上述例子抽象模型,并对应通用的观察者模型

2016-11-01 9 53 52

迭代器模式

迭代器模式可以用 JavaScript 提供了 Iterable Protocol 可迭代协议来表示。Iterable Protocol 不是具体的变量类型,而是一种可实现协议。JavaScript 中像 Array、Set 等都属于内置的可迭代类型,可以通过 iterator 方法来获取一个迭代对象,调用迭代对象的 next 方法将获取一个元素对象,如下示例。

var iterable = [1, 2];

var iterator = iterable[Symbol.iterator]();

iterator.next(); // => { value: "1", done: false}
iterator.next(); // => { value: "2", done: false}

iterator.next(); // => { value: undefined, done: true}

元素对象中:value 表示返回值,done 表示是否已经到达最后。

遍历迭代器可以使用下面做法。

var iterable = [1, 2];
var iterator = iterable[Symbol.iterator]();

var iterator = iterable();

while(true) {
    try {
        let result = iterator.next();  // <= 获取下一个值
    } catch (err) {
        handleError(err);  // <= 错误处理
    }
    if (result.done) {
        handleCompleted();  // <= 无更多值(已完成)
        break;
    }
    doSomething(result.value);
}

主要对应三种情况:

  • 获取下一个值
    调用 next 可以将元素一个个地返回,这样就支持了返回多次值。

  • 无更多值(已完成)
    当无更多值时,next 返回元素中 done 为 true。

  • 错误处理
    当 next 方法执行时报错,则会抛出 error 事件,所以可以用 try catch 包裹 next 方法处理可能出现的错误。

RxJS 的观察者 + 迭代器模式

RxJS 中含有两个基本概念:Observables 与 Observer。Observables 作为被观察者,是一个值或事件的流集合;而 Observer 则作为观察者,根据 Observables 进行处理。
Observables 与 Observer 之间的订阅发布关系(观察者模式) 如下:

  • 订阅:Observer 通过 Observable 提供的 subscribe() 方法订阅 Observable。
  • 发布:Observable 通过回调 onNext 方法向 Observer 发布事件。

下面为 Observable 与 Observer 的伪代码

// Observer
var Observer = {
    next(value) {
        alert(`收到${value}`);
    }
};

// Observable
function Observable (Observer) {
    setTimeout(()=>{
        Observer.next('A');
    },1000)
}

// subscribe
Observable(Observer);

上面实际也是观察者模式的表现,那么迭代器模式在 RxJS 中如何体现呢?

在 RxJS 中,Observer 除了有 next 方法来接收 Observable 的事件外,还可以提供了另外的两个方法:error() 和 complete(),与迭代器模式一一对应。

var Observer = {
    next(value) { /* 处理值*/ },
    error(error) { /* 处理异常 */ },
    complete() { /* 处理已完成态 */ }
};

结合迭代器 Iterator 进行理解:

  • next()
    Observer 提供一个 next 方法来接收 Observable 流,是一种 push 形式;而 Iterator 是通过调用 iterator.next() 来拿到值,是一种 pull 的形式。

  • complete()
    当不再有新的值发出时,将触发 Observer 的 complete 方法;而在 Iterator 中,则需要在 next 的返回结果中,当返回元素 done 为 true 时,则表示 complete。

  • error()
    当在处理事件中出现异常报错时,Observer 提供 error 方法来接收错误进行统一处理;Iterator 则需要进行 try catch 包裹来处理可能出现的错误。

下面是 Observable 与 Observer 实现观察者 + 迭代器模式的伪代码,数据的逐渐传递传递与影响其实就是流的表现。

// Observer
var Observer = {
    next(value) {
        alert(`收到${value}`);
    },
    error(error) {
        alert(`收到${value}`);
    },
    complete() {
        alert("complete");
    },
};

// Observable
function Observable (Observer) {
    [1,2,3].map(item=>{
        Observer.next(item);
    });

    Observer.complete();
    // Observer.error("error message");
}

// subscribe
Observable(Observer);

RxJS 基础实现

有了上面的概念及伪代码,那么在 RxJS 中是怎么创建 Observable 与 Observer 的呢?

创建 Observable

RxJS 提供 create 的方法来自定义创建一个 Observable,可以使用 onNext 来发出流。

var Observable = Rx.Observable.create(observer => {
    observer.onNext(2);
    observer.onCompleted();
    return  () => console.log('disposed');
});

创建 Observer

Observer 可以声明 next、err、complete 方法来处理流的不同状态。

var Observer = Rx.Observer.create(
    x => console.log('Next:', x),
    err => console.log('Error:', err),
    () => console.log('Completed')
);

最后将 Observable 与 Observer 通过 subscribe 订阅结合起来。

var subscription = Observable.subscribe(Observer);

RxJS 中流是可以被取消的,调用 subscribe 将返回一个 subscription,可以通过调用 subscription.unsubscribe() 将流进行取消,让流不再产生。

看了起来挺复杂的?换一个实现形式:

// @Observables 创建一个 Observables
var streamA = Rx.Observable.just(2);

// @Observer streamA$.subscribe(Observer)
streamA.subscribe(v => console.log(v));

将上面代码改用链式写法,代码变得十分简洁:

Rx.Observable.just(2).subscribe(v => console.log(v));

RxJS · Operators 操作

Operators 操作·入门

Rx.Observable.just(2).subscribe(v => console.log(v));

上面代码相当于创建了一个流(2),最终打印出2。那么如果想将打印结果翻倍,变成4,应该怎么处理呢?

方案一?: 改变事件源,让 Observable 值 X 2

Rx.Observable.just(2 * 2 /* <= */).subscribe(v => console.log(v));

方案二?: 改变响应方式,让 Observer 处理 X 2

Rx.Observable.just(2).subscribe(v => console.log(v * 2 /* <= */));

优雅方案: RxJS 提供了优雅的处理方式,可以在事件源(Observable)与响应者(Observer)之间增加操作流的方法。

Rx.Observable.just(2)
             .map(v => v * 2) /* <= */
             .subscribe(v => console.log(v));

map 操作跟数组操作的作用是一致的,不同的这里是将流进行改变,然后将新的流传出去。在 RxJS 中,把这类操作流的方式称之为 Operators(操作)。RxJS提供了一系列 Operators,像map、reduce、filter 等等。操作流将产生新流,从而保持流的不可变性,这也是 RxJS 中函数式编程的一点体现。关于函数式编程,这里暂不多讲,可以看看另外一篇文章 《谈谈函数式编程》

到这里,我们知道了,流从产生到最终处理,可能经过的一些操作。即 RxJS 中 Observable 将经过一系列 Operators 操作后,到达 Observer。

          Operator1   Operator2
Observable ----|-----------|-------> Observer

一系列的 Operators 操作

RxJS 提供了非常多的操作,像下面这些。

Aggregate,All,Amb,ambArray,ambWith,AssertEqual,averageFloat,averageInteger,averageLong,blocking,blockingFirst,blockingForEach,blockingSubscribe,Buffer,bufferWithCount,bufferWithTime,bufferWithTimeOrCount,byLine,cache,cacheWithInitialCapacity,case,Cast,Catch,catchError,catchException,collect,concatWith,Connect,connect_forever,cons,Contains,doAction,doAfterTerminate,doOnComplete,doOnCompleted,doOnDispose,doOnEach,doOnError,doOnLifecycle,doOnNext,doOnRequest,dropUntil,dropWhile,ElementAt,ElementAtOrDefault,emptyObservable,fromNodeCallback,fromPromise,fromPublisher,fromRunnable,Generate,generateWithAbsoluteTime,generateWithRelativeTime,Interval,intervalRange,into,latest (Rx.rb version of Switch),length,mapTo,mapWithIndex,Materialize,Max,MaxBy,mergeArray,mergeArrayDelayError,mergeWith,Min,MinBy,multicastWithSelector,nest,Never,Next,Next (BlockingObservable version),partition,product,retryWhen,Return,returnElement,returnValue,runAsync,safeSubscribe,take_with_time,takeFirst,TakeLast,takeLastBuffer,takeLastBufferWithTime,windowed,withFilter,withLatestFrom,zipIterable,zipWith,zipWithIndex

关于每一个操作的含义,可以查看官网进行了解。下面将举几个例子。

just
just 可以将普通数据转换成流式数据 Observable。如上面的 Rx.Observable.just(2)。

fromEvent
除了数值外,RxJS 还提供了关于事件的操作,fromEvent 可以用来监听事件。当事件触发时,将事件 event 转成可流动的 Observable 进行传输。下面示例表示:监听文本框的 keyup 事件,触发 keyup 可以产生一系列的 event Observable。

var text = document.querySelector('#text');
Rx.Observable.fromEvent(text, 'keyup')
             .subscribe(e => console.log(e));

map
map 方法跟我们平常使用的方式是一样的,不同的只是这里是将流进行改变,然后将新的流传出去。上面示例已有涉及,这里不再多讲。

Rx.Observable.just(2)
             .map(v => 10 * v)
             .subscribe(v => console.log(v));

Rx 提供了许多的操作,为了更好的理解各个操作的作用,我们可以通过一个可视化的工具 marbles 图 来辅助理解。如 map 方法对应的 marbles 图如下

map

箭头可以理解为时间轴,上面的数据经过中间的操作,转变成下面的模样。

FlatMap
FlatMap 也是 RxJS 中常用的接口,我们来结合 marbles 图来理解它

rxjs_flatmap

上面的数据流中,产生了新的分支流(流中流),FlatMap 的作用则是将分支流调整回主干上,最终分支上的数据流都经过主干的其他操作,其实也是将流中流进行扁平化。

FlatMapLatest
FlatMapLatest 与 FlatMap 都是将分支流疏通到主干上,而不同的地方在于 FlatMapLatest 只会保留最后的流,而取消抛弃之前的流。

除了上面提到的 marbles,也可以 ASCII 字符的方式来绘制可视化图表,下面将结合 Map、FlatMap 和 FlatMapLatest 进行对比来理解。

@Map             @FlatMap            @FlatMapLatest
                         ↗  ↗                 ↗  ↗
-A------B-->           a2 b2                a2 b2  
-2A-----2B->          /  /                 /  /  
                    /  /                 /  /
                  a1 b1                a1 b1
                 /  /                 /  /
                -A-B----------->     -A-B---------->
                --a1-b1-a2-b2-->     --a1-b1---b2-->

FlatMap 和 FlatMapLatest 中,A 和 B 是主干上产生的流,a1、a2 为 A 在分支上产生,b1、b2 为 B 在分支上产生,可看到,最终将归并到主干上。FlatMapLatest 只保留最后的流,所以将 A 的 a2 抛弃掉。

Debounce
Debounce 操作可以操作一个时间戳 TIMES,表示经过 TIMES 毫秒后,没有流入新值,那么才将值转入下一个操作。

rxjs_debounce

RxJS 中的操作符是满足我们以前的开发思维的,像 map、reduce 这些。另外,无论是 marbles 图还是用 ASCII 字符图这些可视化的方式,都对 RxJS 的学习和理解有非常大的帮助。

使用 RxJS 一步步实现搜索示例

RxJS 提供许多创建流或操作流的接口,应用这些接口,我们来一步步将搜索的示例进行 Rx 化。

使用 RxJS 提供的 fromEvent 接口来监听我们输入框的 keyup 事件,触发 keyup 将产生 Observable。

var text = document.querySelector('#text');
Rx.Observable.fromEvent(text, 'keyup')
             .subscribe(e => console.log(e));

这里我们并不想输出事件,而想拿到文本输入值,请求搜索,最终渲染出结果。涉及到两个新的 Operators 操作,简单理解一下:

  • pluck('target', 'value')
    将输入的 event,输出成 event.target.value。

  • flatMap()
    将请求搜索结果输出回给 Observer 上进行渲染。

var text = document.querySelector('#text');
Rx.Observable.fromEvent(text, 'keyup')
             .pluck('target', 'value') // <--
             .flatMap(url => Http.get(url)) // <--
             .subscribe(data => render(data))

上面代码实现了简单搜索呈现,但同样存在一开始提及的两个问题。那么如何减少请求数,以及取消已无用的请求呢?我们来了解 RxJS 提供的其他 Operators 操作,来解决上述问题。

  • debounce(TIMES)
    表示经过 TIMES 毫秒后,没有流入新值,那么才将值转入下一个环节。这个与前面使用 setTimeout 来实现函数节流的方式有一致效果。

  • flatMapLatest()
    使用 flatMapLatest 替换 flatMap,将能取消上一个已无用的请求,只保留最后的请求结果流,这样就确保处理展示的是最后的搜索的结果。

最终实现如下,与一开始的实现进行对比,可以明显看出 RxJS 让代码变得十分简洁。

var text = document.querySelector('#text');
Rx.Observable.fromEvent(text, 'keyup')
             .debounce(250) // <- throttling behaviour
             .pluck('target', 'value')
             .flatMapLatest(url => Http.get(url)) // <- Kill the previous requests
             .subscribe(data => render(data))

总结

本篇作为 RxJS 入门篇到这里就结束,关于 RxJS 中的其他方面内容,后续再拎出来进一步分析学习。
RxJS 作为一个库,可以与众多框架结合使用,但并不是每一种场合都需要使用到 RxJS。复杂的数据来源,异步多的情况下才能更好凸显 RxJS 作用,这一块可以看看民工叔写的《流动的数据——使用 RxJS 构造复杂单页应用的数据逻辑》 相信会有更好的理解。点击查看更多文章>>

附:
RxJS(JavaScript) https://github.com/Reactive-Extensions/RxJS
RxJS(TypeScript ) https://github.com/ReactiveX/rxjs

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

构建流式应用—RxJS详解 的相关文章

  • 我如何在 AngularJS 中监听点击并按住的情况?

    我制作了一个时间计数器 您可以通过单击按钮来增加或减少时间 然而 我希望当我单击并按住按钮时 时间的价值会不断攀升 所以目前如果你看到我的Plunkr http plnkr co edit BxX9x5zYFMXVqt5JsN1F p pr
  • 如果对象包含在另一个数组中,则从数组中删除该对象

    我试图从数组中删除一个对象 如果该对象的属性 唯一 包含在另一个数组中 我知道我可以像这样执行嵌套 for 循环 for i 0 i lt array length i for j 0 j lt array2 length j if arr
  • 动态速度计 javascript 或 jquery 插件

    我希望有动态ajax插件在页面上显示速度计 一个想法是我设置一个背景并旋转针 有人知道相关插件吗 这里有一些供您参考 http bernii github com gauge js http bernii github com gauge
  • 非 DOM 对象上的 jQuery 自定义事件

    我最近阅读了一些代码 其功能如下 bob name Bob Smith rank 7 bob bind nameChanged function bob trigger nameChanged 这似乎有效 但我在 jQuery 文档或源代码
  • 消息“在 jest.setTimeout 指定的 5000 毫秒超时内未调用异步回调”

    我正在使用 Puppeteer 和 Jest 来运行一些前端测试 我的测试如下 describe Profile Tab Exists and Clickable settings user gt test Assert that you
  • 在网页上的文本框中键入内容时删除所有空格

    我如何在用户打字时即时删除输入到文本框中的空格 function var txt myTextbox var func function txt val txt val replace s g txt keyup func blur fun
  • jquery 验证错误位置

    这看起来很简单 但我无法弄清楚 我正在使用 jquery 验证插件 我验证所有文件 但我想要的是在输入文本行中显示验证消息警报 例如在电子邮件输入中 请填写电子邮件地址 但现在它出现在所有字段下 在我的html中
  • 如何仅在 NextJS 站点构建期间使用 getInitialProps?

    当使用 NextJS 构建静态站点时 我想要getInitialProps方法仅在构建步骤期间触发 而不是在客户端上触发 在构建步骤中 NextJS 运行getInitialProps 方法 https nextjs org docs fe
  • Leaflet js虚构地图

    我是 Leaflet 的新手 我想了解如何创建完全交互式的虚构地图 我有一张图像想要转换为传单地图 该图像基本上像图表一样具有许多连接和点 我想首先将该图像转换为地图 能够将鼠标悬停在这些点上 突出显示它们并显示有关它们的信息 并且还可以在
  • jquery window.open 在 ajax 成功中被阻止

    尝试在我的 ajax 成功调用中打开一个新的浏览器窗口 但是 它被阻止为弹出窗口 我做了一些搜索 发现用户事件需要绑定到 window open 才能避免这种情况发生 我还找到了这个解决方案 您可以在 ajax 之前打开一个空白窗口 然后在
  • 通过 node-http-proxy 保留基于 cookie 的会话

    我有一个简单的基于 Express 的 Node js Web 服务器 用于开发 JavaScript 应用程序 我将服务器设置为使用 node http proxy 来代理应用程序向在不同域和端口上运行的 Jetty 服务器发出的 API
  • Google Chrome 106 可拖动导致元素消失

    使用拖放元素时 绝对定位元素中包含的大多数其他元素都会从屏幕上消失 如果我调整窗口大小 这些元素会出现 但在开始拖动时会再次消失 我在最新版本的 Google Chrome 106 和 Beta 版本 107 0 5304 18 以及现在的
  • 如何在 Angular 中从父组件访问子组件?

    I have mat paginator在子组件a中 如下所示 子组件 html
  • Typeahead.js substringMatcher 函数说明

    我只是在做一些研究Typeahead js这是一个非常酷的图书馆 感谢文档 我已经成功地获得了一个基本的示例 该文档也非常好 但是我试图弄清楚以下代码块实际上在做什么 var substringMatcher function strs r
  • 将数组排序为第一个最小值、第一个最大值、第二个最小值、第二个最大值等

    编写一个JS程序 返回一个数组 其中第一个元素是第一个最小值 第二个元素是第一个最大值 依此类推 该程序包含一个函数 该函数接受一个参数 一个数组 该函数根据要求返回数组 输入示例 array 2 4 7 1 3 8 9 预期输出 1 9
  • Twitter 嵌入时间轴小部件

    我继续下载http platform twitter com widgets js http platform twitter com widgets js And the http platform twitter com embed t
  • 为什么“tbody”不设置表格的背景颜色?

    我在用 tbody 作为 CSS 选择器来设置background color在一个表中 我这样做是因为我有多个 tbody 表内的部分 它们具有不同的背景颜色 我的问题是 当使用border radius在细胞上 细胞不尊重backgro
  • JavaScript 代码在不使用 ActiveX 的情况下截取网站屏幕截图

    我有一个用户与之交互的 JavaScript 应用程序 我需要保存当前界面的外观 裁剪出我需要的部分 或者通过指定div只拍摄我需要的部分 然后发送回服务器 显然任何外部服务都无法做到这一点 我需要一个 JavaScript 或Flash
  • 如何在 javascript 正则表达式中匹配平衡分隔符?

    我原以为这个问题是不可能的 据我所知 Javascript 的正则表达式既没有递归插值 也没有漂亮的 NET 平衡组功能 但问题就在那里 如问题 12 所示正则表达式 alf nu http regex alf nu 匹配平衡对 lt an
  • 如何在 gulp.src 中使用基本正则表达式?

    我正在尝试选择两个文件gulp src highcharts js and highcharts src js 当然 我知道我可以使用数组表达式显式添加这两个表达式 但出于学习目的 我尝试为它们编写一个表达式 我读过可以使用简单的正则表达式

随机推荐

  • .NET6.0的日志组件Log4net

    前言 不允许让没有任何日志监控的项目上线 所以说真正开发起来必须要有监控 相当于多一双眼睛帮着我们看项目在运行时会不会有什么问题 我们要不断的在每个环节写日志 这样发生异常我们可以快速知道哪里有问题了 就可以快速解决 目录 一 Log4ne
  • addEventListener()方法第三个参数(useCapture)与浏览器事件触发阶段

    最近面试中有被问到EventTarget addEventListener 方法的第三个参数useCapture 此方法就不着重讲解了 同时该方法的第三个参数可以传一个对象 可以参考mdn文档 讲解的很详细EventTarget addEv
  • 数据挖掘知识浅析

    一 什么是数据挖掘 数据挖掘是指从大量数据中提取或 挖掘 知识 数据挖掘是一种 黄金挖掘 从沙子堆中挖掘出黄金 找出最有价值的黄金 这种有机的价值物提取的过程称为 黄金挖掘 通过某种手段或者经验丰富人士 从海量的数据中找出有用的 数据 掌握
  • Markdown入门2-标题、引用、列表、代码、分隔线

    区块元素 网上推荐了很多撰写Markdown文件的软件 根据个人爱好可以自己去选一款 小编觉得选哪个都差不多 能出来效果就行呗 以下相关代码测试小编是在有道云笔记上进行的 我会把代码贴出来方便大家演练 1 标题 Markdown 支持两种标
  • fastadmin隐藏table操作栏拖动排序按钮

    记录 fastadmin隐藏table操作栏拖动排序按钮 在 js 文件中的 Table api init 中添加一下代码 dragsort url
  • C++ 第一阶段编程练习

    目录 1 1 编程练习 1 1 1 编程题目 第一题 第二题 第三题 第四题 第五题 第六题 第七题 1 2 编程讲解 1 2 1 题目一 1 2 2 题目二 1 2 3 题目三 1 2 4 题目四 1 2 5 题目五 1 2 6 题目六
  • rtklib源码 rtk差分解算,rtkpos和replos函数流程梳理

    rtklib源码 rtk差分解算 rtkpos和replos函数流程梳理 rtkpos函数梳理 总体流程 replos函数梳理 replos总体流程 1 通过satposs函数计算卫星的位置 速度等参数 2 通过zdres函数计算基站伪距和
  • 提升mysql服务器性能(系统参数与文件系统优化方案)

    加快TCP链接的回收 不应该使用CFQ cfq 完全公平队列 是anicipaory模式的替代品 没有过多的做预测性调度 而是根据给定的进程io优先级 直接来分配操作的顺序 最好使用 防止饥饿 xfs据说比较好 v
  • C# --- Struct and Record

    C Struct and Record Struct Record Struct struct是一种数据类型 和class非常类似 主要有以下的不同 struct是value type class是reference type 因为是val
  • log4j2配置文件

  • 机器学习-线性回归-多元梯度下降法

    目录标题 线性回归 多元线性回归 正规方程 线性回归 我们的回归方程常写成如下形式 h x 0 1 X 代价函数 J 12 i 1m h x i y i 2 看看代价函数到底是在干什么 如图 梯度下降是一个用来求函数最小值的算法 我们将使用
  • Echarts-折线图-设置线条颜色以及线条以下区域显示渐变颜色

    首先 先看折线图效果 1 设置线条颜色 在series中 数组项设置lineStyle属性 lineStyle 设置线条的style等 normal color red 折线线条颜色 红色 2 设置线条上点的颜色 也是图例的颜色 在seri
  • [OpenAirInterface实战-1] :什么是OAI?OAI常见问题解答

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 120490410 目录 前言 什么是软
  • 上拉加载原理

    实现思路 之前写过一篇触底加载 经过一番苦学钻研 优化一下 样式方面 滚动区域是给固定高度 设置 overflow y auto 来实现 接下来看看js方面的实现 其实也很简单 触发的条件是 可视高度 滚动距离 gt 实际高度 例子我会使用
  • 网络工程师学习笔记-------关于T1和E1载波

    关于T和E载波 标准模拟话音信号频率为4KHz 为了对模拟语音进行数字化 必须按至少8KHz的速率采样 采用7Bit进行量化即128级量化 则每个信道的比特率为 8KHz 7Bit 56Kb s 为了每一个这样的低速信道安装一条通信线路是不
  • 机器学习F1值的概念

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 一 什么是F1 score 二 计算过程 1 首先定义以下几个概念 2 通过第一步的统计值计算每个类别下的precision和recall 3 通过第二步计算结果计
  • openstack-nova-compute.service起不来

    1 启动服务 2 查看compute nova日志tail var log nova nova compute log 发现身份验证机制AMQPLAIN拒绝登录 3 关闭防火墙 root controller systemctl stop
  • 资讯汇总230217

    230217 22 48 美联储理事鲍曼 美国通胀仍旧太高 美联储理事鲍曼表示 美国通胀仍旧太高 美国当前的经济数据不一致 不同寻常的低失业率是一个好迹象 让通胀回到目标还有很长的路要走 需要继续加息 直到看到更多进展 财联社 230217
  • 西门子PLC如何与多个三菱PLC建立无线通信?

    对一个大型工厂 由于生产线的不断改造 新老流程的不断更新 这些PLC系统往往是由不同的制造商提供的 那么在智慧工厂的实现中 常会遇到不同品牌PLC之间需要进行相互通讯的情况 由于场地和生产能效的原因 在后期的系统改造中 通常需要采用无线的方
  • 构建流式应用—RxJS详解

    最近在 Alloyteam Conf 2016 分享了 使用RxJS构建流式前端应用 会后在线上线下跟大家交流时发现对于 RxJS 的态度呈现出两大类 有用过的都表达了 RxJS 带来的优雅编码体验 未用过的则反馈太难入门 所以 这里将结合