Обойти более 6 параметров forkJoin?

У меня есть потребность / желание использовать более шести параметров в forkJoin. В настоящее время, основываясь на ответе на другой связанный вопрос, представляется невозможным отправить более 6 параметров в forkJoin.

Однако на основании официальной документации говорится, что "forkJoin - это оператор, который принимает любое количество наблюдаемых, которые могут быть переданы либо в виде массива, либо непосредственно в качестве аргументов".

forkJoin - официальные документы

Что ж, я делаю это и получаю ошибку TS2322: тип 'foo' нельзя назначить типу 'bar[]'.

В своем исследовании я также обнаружил, что лучше не посылать аргументы в виде массива, если у вас есть обещания, которые возвращают разные типы, так как это приведет к приведению их ко всем типам. - Источник

Вот мой код Я использую последнюю версию Typescript и Angular 4.

ngOnInit() {
    this.spinner.show();
    Observable.forkJoin(
        this.loadParams(),              // Returns an Observable<Object>
        this.service.getPromiseType1(), // The rest return Observable<Array>
        this.service.getPromiseType2(),
        this.service.getPromiseType3(),
        this.service.getPromiseType4(),
        this.service.getPromiseType5(),
        this.service.getPromiseType6(),
    ).finally(() => this.spinner.hide())
        .subscribe(
            ([param, promise1, promise2, promise3, promise4, promise5, promise6]) => {
                this.job = job;
                this.value1 = promise1;
                this.value2 = promise2;
                this.value3 = promise3;
                this.value4 = promise4;
                this.value5 = promise5;
                this.value6 = promise6;
            }, (error) => {errorHandlingFunction}
   });

Если я удаляю какой-либо один параметр, чтобы он передавал шесть параметров в forkJoin, он работает нормально. Поэтому мой вопрос в том, что в моем случае, когда я хочу загрузить наблюдаемую объектную и последующие наблюдаемые массивы всего за один вызов, есть ли другой способ сделать это? Это ошибка с forkJoin, поскольку в официальной документации сказано, что она должна принимать любое количество наблюдаемых?

Я попытался создать массив типа Observable и использовать array.forEach() внутри forkJoin, но он жалуется на возвращение типа void. В любом случае это выглядело как ненормальный способ сделать это.

4 ответа

Решение

Как объясняет ответ в связанном вопросе, максимальное количество аргументов ограничено только определениями типов, а не самим источником времени выполнения. Определения типов полезны, потому что они объявляют типы элементов массива, которые будут созданы для следующего шага наблюдаемого потока (типы для [param, promise1, promise2, ...] в твоем случае).

Похоже, что строгая безопасность типов вокруг назначений в обработчике подписки - вот то, что вызывает проблему для вас. Поскольку у вас есть более 6 наблюдаемых, по умолчанию результирующие параметры имеют общий тип, который, вероятно, не соответствует типам полей, которые вы пытаетесь назначить.

Есть несколько способов обойти это. Вы можете привести аргументы в свой обработчик подписки или добавить свои собственные типы самостоятельно. Приведение аргументов - это быстрое и грязное решение, но оно приводит к потере безопасности типов. Добавление типов самостоятельно позволит вам поддерживать безопасность типов, но это также может означать, что вы получите любое количество фабричных объявлений методов. Поместите ниже в файл определения типа (*.d.ts) в любом месте вашего проекта. Мне нравится помещать определения типа, как это в typings/ каталог на уровне одного брата к моему app/ каталог.

import { Observable, SubscribableOrPromise } from 'rxjs/Observable';

declare module 'rxjs/observable/ForkJoinObservable' {
    namespace ForkJoinObservable {
        export function create<T, T2, T3, T4, T5, T6, T7>(v1: SubscribableOrPromise<T>, v2: SubscribableOrPromise<T2>, v3: SubscribableOrPromise<T3>, v4: SubscribableOrPromise<T4>, v5: SubscribableOrPromise<T5>, v6: SubscribableOrPromise<T6>, v7: SubscribableOrPromise<T7>): Observable<[T, T2, T3, T4, T5, T6, T7]>;
        export function create<T, T2, T3, T4, T5, T6, T7, R>(v1: SubscribableOrPromise<T>, v2: SubscribableOrPromise<T2>, v3: SubscribableOrPromise<T3>, v4: SubscribableOrPromise<T4>, v5: SubscribableOrPromise<T5>, v6: SubscribableOrPromise<T6>, v7: SubscribableOrPromise<T7>, project: (v1: T, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7) => R): Observable<R>;
    }
}

Этот процесс более подробно объясняется на странице документации TypeScript для слияния деклараций.


Изменить: Похоже, я использую более старую версию RxJS, и структура немного изменилась с тех пор. Следующее должно быть ближе к объявлениям типов, которые должны работать с текущей структурой:

declare module 'rxjs/Observable' {
    namespace Observable {
        export function forkJoin<T, T2, T3, T4, T5, T6, T7>(sources: [ObservableInput<T>, ObservableInput<T2>, ObservableInput<T3>, ObservableInput<T4>, ObservableInput<T5>, ObservableInput<T6>, ObservableInput<T7>]): Observable<[T, T2, T3, T4, T5, T6, T7]>;
        export function forkJoin<T, T2, T3, T4, T5, T6, T7>(v1: ObservableInput<T>, v2: ObservableInput<T2>, v3: ObservableInput<T3>, v4: ObservableInput<T4>, v5: ObservableInput<T5>, v6: ObservableInput<T6>, v7: ObservableInput<T7>): Observable<[T, T2, T3, T4, T5, T6, T7]>;
    }
}

Я основываю их на текущих объявлениях типа forkJoin.

Что касается расширения модуля, приведенный выше код модифицирует объявления типов для модуля, определенного абсолютным путем 'rxjs/Observable', Это так же, как путь импорта, который вы использовали бы при импорте Observable учебный класс. Модуль, определенный RxJS, экспортирует Observable класс и его поля. Наше дополнение к этому модулю изменяет его, используя namespace блок. Это позволяет нам добавлять объявления типов под Observable пространство имен (например, объявления типа для Observable.myFunctionOrField), который выглядит так же, как вызов статических функций для него. По сути, это объявляет дополнительный Observable.forkJoin Функциональные возможности.

Начиная с версии 6.5 rxJs, вы можете использовать словарь значений, чтобы поместить любое количество наблюдаемых внутри forkJoin, см. Первый пример здесь

    forkJoin({first: of(1), second: of(2), third: of(3)})
             .subscribe(result => {console.log(result.first)});

Спасибо @MikeHill за то, что меня направили в правильном направлении. Решение, которое закончилось для меня, состояло в том, чтобы добавить typings.d.ts в папку src, сгенерированную angular-cli. Я полагаю, что вы также можете использовать папку для ввода текста в том же месте, но вам нужно будет обновить файл tsconfig.app.json, см. Эту статью для получения дополнительной информации. https://github.com/angular/angular-cli/blob/6449a753641340d8fc19a752e1a1ced75f974efa/docs/documentation/1-x/stories/third-party-lib.md

typings.d.ts для 7 параметров с forkJoin

import { ObservableInput, Observable } from 'rxjs';
import { forkJoin } from 'rxjs/internal/observable/forkJoin';

declare module 'rxjs/internal/observable/forkJoin' {
  export function forkJoin<T, T2, T3, T4, T5, T6, T7>(
    sources: [
      ObservableInput<T>,
      ObservableInput<T2>,
      ObservableInput<T3>,
      ObservableInput<T4>,
      ObservableInput<T5>,
      ObservableInput<T6>,
      ObservableInput<T7>
    ],
  ): Observable<[T, T2, T3, T4, T5, T6, T7]>;
  export function forkJoin<T, T2, T3, T4, T5, T6, T7>(
    v1: ObservableInput<T>,
    v2: ObservableInput<T2>,
    v3: ObservableInput<T3>,
    v4: ObservableInput<T4>,
    v5: ObservableInput<T5>,
    v6: ObservableInput<T6>,
    v7: ObservableInput<T7>,
  ): Observable<[T, T2, T3, T4, T5, T6, T7]>;
}

Соединения вил могут быть вложены в логические группы из шести или менее параметров. Следующее должно работать (но не проверено):

ngOnInit() {
    this.spinner.show();
    Observable.forkJoin(
        this.loadParams(),
        Observable.forkJoin(
            this.service.getPromiseType1(),
            this.service.getPromiseType2(),
            this.service.getPromiseType3(),
        ),
        Observable.forkJoin(
            this.service.getPromiseType4(),
            this.service.getPromiseType5(),
            this.service.getPromiseType6(),
        )
    )
    .finally(() => this.spinner.hide())
    .subscribe(payloads => {
        [
            this.job,
            [
                this.value1,
                this.value2,
                this.value3,
            ],
            [
                this.value4,
                this.value5,
                this.value6,
            ],
        ] = payloads
    }, (error) => {
        errorHandlingFunction
    });
}

Вы уверены, что 6 предел?

Этот пример с 10 Observables, используемыми в forkJoins, похоже, работает

const oArray = 
[ ... new Array(10).keys() ]
.map(n => Observable.of(n))
.reduce((obsArray, obs) => {
    obsArray.push(obs);
    return obsArray
}, new Array<Observable<number>>())

Observable
.forkJoin(oArray)
.subscribe(console.log, console.error, () => console.log('DONE'))

Плюс я не понимаю finally оператор вы используете. У меня его нет в RxJs 5.5.2

Другие вопросы по тегам