Observables vs Promises - обработка, затем возвращение асинхронного результата - PullRequest
0 голосов
/ 12 июня 2019

У меня был довольно небольшой опыт работы с обещаниями в AngularJS, и сейчас я пытаюсь разобраться с Observables в Angular.

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

Ниже приведен примеро том, как я мог бы достичь чего-то, используя цепочку обещаний.Задачи:

  1. MyClass.value следует всегда установить, когда doSomethingAsync() вызывается
  2. Вызывающий абонент - в этом случае doSomethingElse() должен иметь возможностьчтобы зацепить заднюю часть doSomethingAsync() , если он хочет и , дождаться завершения , прежде чем использовать результат

export class MyClass {
    private value;

    public doSomethingAsync() {
        return someAsyncCall()
            .then(value => {
                this.value = value
            });
    }
}

export class MyOtherClass {
    public doSomethingElse() {
        const myObj = new MyClass();
        let myValue;

        myObj
            .doSomethingAsync()
            .then(value => {
                myValue = value
            });
    }
}

Я пытаюсь выяснить, как бы я это сделал, используя Observables.

Я мог бы использовать .pipe() с tap() в doSomethingAsync() для захвата значения, но проблема в том, что это победилоне будет выполнено, если doSomethingElse() не вызовет subscribe() в возвращаемом Наблюдаемом.Я не хочу, чтобы doSomethingAsync() зависело от того, что будет делать вызывающий абонент.

Я мог бы вызвать subscribe() в doSomethingAsync(), чтобы получить значение, но тогда я больше не будуиметь наблюдаемый, чтобы вернуться.Поэтому я думаю, что мне, возможно, придется сделать что-то вроде этого:

export class MyClass {
    private value;
    private valueSubject: Subject<any> = new Subject<any>();

    public doSomethingAsync() {
        someAsyncCall()
            .subscribe(value => {
                this.value = value;
                this.valueSubject.next(value);
            });

        return valueSubject.asObservable();
    }
}

export class MyOtherClass {
    public doSomethingElse() {
        const myObj = new MyClass();
        let myValue;

        myObj
            .doSomethingAsync()
            .subscribe(value => {
                myValue = value
            });
    }
}

Но это кажется слишком сложным, так как мне теперь также приходится возиться с Субъектами и эффективно иметь 2 различных потока, которые нужно отслеживать.

Кто-нибудь может предложить лучший способ сделать это?Мне кажется, что с обещаниями гораздо легче справиться ...?

Ответы [ 2 ]

0 голосов
/ 13 июня 2019

ОК, я понял, как это сделать сейчас.Однако теперь я пришел к выводу (см. Ниже), что это не очень хороший способ использовать RxJS ...

export class MyClass {
    private value;

    public doSomethingAsync() {
        const observable = someAsyncCall()
            .pipe(
                tap(value => {
                    this.value = value;
                }),
                share()
            );

        observable.subscribe();

        return observable;
    }
}

export class MyOtherClass {
    public doSomethingElse() {
        const myObj = new MyClass();
        let myValue;

        myObj
            .doSomethingAsync()
            .subscribe(value => {
                myValue = value
            });
    }
}

Как это работает:

  1. Подписка в пределах doSomethingAsync() означает, что асинхронная операция с источником всегда запускается.
  2. Использование share() означает, что вызывающая сторона может также подписаться без повторной активации источника.share() использует Subject factory под капотом, что также означает, что вызывающая сторона может при необходимости повторить попытку всей последовательности.
  3. Используя tap() (вместо subscribe()) в doSomethingAsync() для захвата значения,означает, что он будет перехвачен, если вызывающий абонент повторяет попытку - это сохраняет все состояния согласованными.

Преимущества:

  1. doSomethingAsync() не нужно подписыватьсяесли мы не заинтересованы непосредственно в результате - он все равно выполнит операцию.
  2. MyClass отвечает за поддержание своего собственного состояния - состояние MyClass равно , а не зависит от того, выполняет ли вызывающая функция .subscribe() или нет.

Недостатки:

  1. Это нетипичное поведение - Наблюдатели обычно должны быть подписаныдо того, как они что-то сделают, таким образом это как бы нарушает это ожидание.
  2. Состояние MyClass равно , все еще затрагивается, если вызывающий абонент повторяет подписку - это разработано для поддержания согласованностисостояние, но это делаетзначит, я не полностью не сохранил инкапсуляцию.

Заключение:

Теперь я пришел к выводу, что "правильный" способ сделать это -не идя вразрез с тем, как я думаю, что Observables должны работать - на самом деле полагаться на вызывающего абонента для выполнения подписки, а именно:

export class MyClass {
    private value;

    public doSomethingAsync() {
        return someAsyncCall()
            .pipe(
                tap(value => {
                    this.value = value;
                })
            );
    }
}

export class MyOtherClass {
    public doSomethingElse() {
        const myObj = new MyClass();
        let myValue;

        myObj
            .doSomethingAsync()
            .subscribe(value => {
                myValue = value
            });
    }
}

Преимущества:

  1. Это меньше кода, чем в предыдущем решении.
  2. Поскольку метод doSomethingAsync() возвращает Observable, я думаю, что обычное ожидание будет состоять в том, что ничего не произойдет, пока вы не вызовете .subscribe() для результата.
  3. Вызывается вызывающей стороной.
  4. Я думаю, что в этом контексте возвращаемое Observable может по праву считаться «другом» MyClass и, следовательно, может изменять его состояние (с помощью метода .subscribe())..

Недостатки:

  1. Все еще немного больше кода, чем эквивалент обещания.
  2. Концептуально сложнее для понимания, чем эквивалент обещания:
    • NНужно понимать pipe(), tap() и subscribe()
    • Нужно хотя бы базовое понимание асинхронной обработки потока.
    • Для обещаний вам нужно только понять, как объединять отдельные асинхронные потоки.вызовы вместе с использованием promise.then(), что мне кажется гораздо более простой концепцией.
    • Большинство (если не все) возможностей обработки stream в RxJS излишни - и просто добавляютсложность - если вы на самом деле не имеете дело с потоком событий .
  3. Звонящий должен позвонить subscribe(), чтобы что-то произошло, независимо от того, волнует ли ихо результате (или завершении) асинхронной операции.
0 голосов
/ 12 июня 2019

Как вы сказали, они очень заинтересованы в использовании rxjs.Для классического бэкэнда, где вы просто хотите позвонить один раз, разрешите обещание, и все, преобразуйте наблюдаемое в обещание и верните свое обещание, как того требует ваш дизайн;чтобы достичь такой цели, используйте toPromise () от rxjs над Observable.

Идея сохранения Observable состоит в том, что вы продолжаете наблюдать конечную точку, которая продолжает возвращать значения в случае изменения данных ивы получаете их на лету, и когда вы закончите, вы отписываетесь (оставляя представление или что-то в этом роде).Например, некоторые API-интерфейсы с использованием веб-сокетов или какой-либо серверной части реального времени, такой как Firebase.Но это не имеет смысла для классического бэкэнда, где вы вызываете конечную точку -> вы получаете результат -> вот и все.Потому что, в конце концов, каждый раз, когда вы хотите получить то, что есть, вы должны сделать новый вызов.

Тогда ваш пример будет выглядеть примерно так:

export class MyClass {
    private value;

    public doSomethingAsync() {
        return someAsyncCall().toPromise();
    }
}

export class MyOtherClass {
    public doSomethingElse() {
        const myObj = new MyClass();
        let myValue;

        myObj
            .doSomethingAsync()
            .then(value => {
                myValue = value
            });
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...