携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情
operators
Operators是Rxjs的精华,而且数量众多,源码的代码量也是巨大,但是没有关系,我们先来整体上梳理一下Operator的源码,从几个主要的点入手,后面会用代码实现几个operators。 因为Operator要作为参数在pipe中使用,所以先来看一下Observable中的pipe函数。
class Observable {
// ...
pipe(): Observable<T>;
pipe<A>(op1: OperatorFunction<T, A>): Observable<A>;
pipe<A, B>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>): Observable<B>;
pipe<A, B, C>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>): Observable<C>;
pipe<A, B, C, D>(
op1: OperatorFunction<T, A>,
op2: OperatorFunction<A, B>,
op3: OperatorFunction<B, C>,
op4: OperatorFunction<C, D>
): Observable<D>;
// ...
pipe<A, B, C, D, E, F, G, H, I>(
op1: OperatorFunction<T, A>,
op2: OperatorFunction<A, B>,
op3: OperatorFunction<B, C>,
op4: OperatorFunction<C, D>,
op5: OperatorFunction<D, E>,
op6: OperatorFunction<E, F>,
op7: OperatorFunction<F, G>,
op8: OperatorFunction<G, H>,
op9: OperatorFunction<H, I>,
...operations: OperatorFunction<any, any>[]
): Observable<unknown>;
}
先来看一下pipe函数。pipe函数的签名有很多,是为了辅助类型的自动推到,主要pipe传入的参数在9个以内,就可以正确的推到出类型,如果超过9个,自动推导就失效;额,需要使用者自己指定类型。
// node_modules/rxjs/src/internal/Observable.ts
pipe(...operations: OperatorFunction<any, any>[]): Observable<any> {
return pipeFromArray(operations)(this);
}
// node_modules/rxjs/src/internal/util/identity.ts
export function identity<T>(x: T): T {
return x;
}
// node_modules/rxjs/src/internal/util/pipe.ts
/** @internal */
export function pipeFromArray<T, R>(fns: Array<UnaryFunction<T, R>>): UnaryFunction<T, R> {
if (fns.length === 0) {
return identity as UnaryFunction<any, any>;
}
if (fns.length === 1) {
return fns[0];
}
return function piped(input: T): R {
return fns.reduce((prev: any, fn: UnaryFunction<T, R>) => fn(prev), input as any);
};
}
大概的逻辑是,通过数组的reduce方法依次执行所有的操作符,因为操作符是有顺序的,所以执行的逻辑是将上一个操作符方法返回的值作为下一个操作符的参数,就像一个管道一样把所有的操作符串联起来,这里正是体现了函数式编程。
我们来实现一下,集中在核心逻辑。我们只考虑map操作符。
map
开始撸代码了。
class Observable {
constructor(subscribe){
...
}
subscribe(observer) {
...
}
// 返回一个新的Observable
pipe(...operatorFuncs) {
return new Observable((observer) => {
this.subscribe({
next(data) {
function piped(input){
return operatorFuncs.reduce((prev, fn) => fn(prev), input);
}
let res = piped(data);
observer.next(res);
},
error(err){
observer.error(err);
},
complete(){
observer.complete();
}
});
});
}
}
function map(func){
return (data) => {
return func(data);
};
}
可以做一些测试,发现可以正常工作。
of
of是创建一个静态的Observable。
function of(...args) {
return new Observable((observer) => {
try {
args.forEach((arg) => {
observer.next(arg);
});
}
catch(error){
observer.error(error);
}
observer.complete();
});
}
interval
先来看一下interval的原文介绍。
interval
returns an Observable that emits an infinite sequence of
ascending integers, with a constant interval of time of your choosing
between those emissions. The first emission is not sent immediately, but
only after the first period has passed. By default, this operator uses the
async` {@linkSchedulerLike} to provide a notion of time, but you may pass any
{@linkSchedulerLike} to it.
要注意一下,这个interval是异步的哦。
export function interval(period = 0, scheduler: SchedulerLike = asyncScheduler): Observable<number> {
if (period < 0) {
// We cannot schedule an interval in the past.
period = 0;
}
return timer(period, period, scheduler);
}
实现我们自己的interval。由于我们之前实现了自己的AsyncScheduler,这里正好使用一下它。
function interval(period, scheduler){
return new Observable((observer) => {
let count = 0;
if(!scheduler) {
scheduler = new AsyncScheduler('repeat');
}
scheduler.schedule(() => {
observer.next(count++);
}, period ? period : 0);
});
}
本篇文章简单过了一下Rxjs里操作符的部分源码,实现了map,of,interval几个操作符,其实其他的操作符也比较类似,只是工作量大小的问题。