Как я могу использовать Observables вместо обещаний?

У меня есть служба с некоторыми методами, большинство из них требуют завершения определенного обратного вызова, прежде чем он сможет выполнять свои функции. С промисами в псевдо это очень легко сделать:

ready = http.get(stuff); // Returns a promise, resolves after a while

methodOne() { // methods sometimes called before promise resolves
    this.ready.then(_ => { 
        // doStuff
    });
}

methodTwo() {
    return this.ready.then(d => {
        // doOtherStuff
    });
}

В основном мне нужно делать вещи, только когда я уверен, что сервис готов. На самом деле мне нужно только проверить, готов ли он (что делает methodOne, просто иллюстрируя с помощью methodTwo, что также легко добавить больше вещей).

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

Обещания запомнят значение и узнают, было ли оно разрешено. Observable несколько сложнее, и кажется, что создать такой же поток проблематично. Мне нужно, чтобы все, что подписывается на Observable, было известно, когда оно будет готово. Иногда метод вызывается рано — до того, как Observable испустит, а иногда поздно, после того, как Observable уже испущен.

У меня есть это прямо сейчас, но это, похоже, не работает:

this.ready$ = someObservable // Will fire after a litle while but never finish - i only need the first to check though.
  .publishReplay(1).refCount(); // Trying to replay if subscription comes after emit.

this.ready$.subscribe(_ => {
    // This will be called
});

methodOne() { 
    this.ready$.subscribe(_ => {
        // Not called
    });
};

Возможно, я неправильно понял использование publishReplay и refCount?


person Per Hornshøj-Schierbeck    schedule 02.08.2017    source источник
comment
Поскольку я работаю над проектом Angular, предпочтительным способом является использование Observables Кто предпочитает? С каким обоснованием? И какие подтверждающие данные?   -  person T.J. Crowder    schedule 02.08.2017
comment
@ T.J.Crowder Я отредактирую свое заявление, поскольку оно может быть субъективным, но я видел, что в нем упоминалось несколько мест. stackoverflow.com/questions/37364973/   -  person Per Hornshøj-Schierbeck    schedule 02.08.2017
comment
Я немного поторопился с голосованием за закрытие - мне нужно решение с Observables, формулировка может (и была) изменена, поэтому она больше не основана на мнении.   -  person Per Hornshøj-Schierbeck    schedule 02.08.2017
comment
Действительно ли Observables является хорошей заменой Promises, на первый взгляд, нужно спросить мнение. (Кстати, это не было моим закрытым голосованием.) Если вопрос действительно в том, как я могу использовать Observables вместо Promises? Я бы отредактировал заголовок вопроса, чтобы сказать это. Я бы также отредактировал, чтобы сделать его более кратким. (И одно лицо, выражающее мнение в ответе SO, не считается разумным обоснованием с подтверждающими данными.)   -  person T.J. Crowder    schedule 02.08.2017
comment
@ T.J.Crowder Хорошо, название было изменено.   -  person Per Hornshøj-Schierbeck    schedule 02.08.2017
comment
@PerHornshøj-Schierbeck Используйте Promise, если хотите получить результат только один раз. Используйте Observables, если вы хотите много раз получить результат, похожий на обещание. Observable также дает возможность использовать перехватчики и другие полезные вещи, в то время как промисы этого не делают. На фундаментальном уровне это единственные различия.   -  person Lansana Camara    schedule 02.08.2017


Ответы (1)


Я думаю, что вы ищете AsyncSubject. Он очень хорошо имитирует поведение promises. Вот описание:

AsyncSubject — это вариант, в котором наблюдателям отправляется только последнее значение выполнения Observable и только после завершения выполнения.

Вот как это можно использовать в вашем случае:

subject = new AsyncSubject();
ready = streamOfData(stuff).first().subscribe(subject);    
methodOne() {
    return this.subject.asObservable();
}

Субъект подписывается на базовую наблюдаемую, возвращаемую оператором first, и ждет, пока она не будет завершена. Он собирает всех подписчиков, но не отправляет им никаких значений. Как только основной наблюдаемый объект завершается, он запоминает значение и отправляет его собранным подписчикам. Всем новым будущим подписчикам будет немедленно передано это сохраненное разрешенное значение.

Вот простой пример, который демонстрирует, что вы можете подписаться до или после завершения наблюдаемого объекта:

const subject = new AsyncSubject();
const o = subject.asObservable();
o.subscribe((v) => {
  console.log(v);
});
interval(500).first().subscribe(subject);

setTimeout(() => {
  o.subscribe((v) => {
    console.log(v);
  });
}, 2000);
person Max Koretskyi    schedule 02.08.2017
comment
Я должен упомянуть, что пример не загружает HTTP-запрос — это действительно поток, который никогда не заканчивается. Я обновлю свой вопрос, чтобы отразить это более четко. Думаю, я мог бы использовать .first() на моем наблюдаемом... Я попробую. - person Per Hornshøj-Schierbeck; 02.08.2017
comment
@ PerHornshøj-Schierbeck, так как же вы должны были использовать обещания с потоком, который никогда не заканчивается? Обещания — это все об одном разрешенном значении. - person Max Koretskyi; 02.08.2017
comment
Я бы обернул первый выброс и затем разрешил обещание - person Per Hornshøj-Schierbeck; 02.08.2017
comment
@ PerHornshøj-Schierbeck, хорошо, тогда просто используй first(). Обновлен мой ответ - person Max Koretskyi; 02.08.2017
comment
Это ведет себя точно так же, как моя собственная попытка с replaySubject(1).refCount(). Он работает с промисами (только что протестировано), но оба примера Observable работают только в том случае, если подписка (ваш методOne) вызывается до первого/единственного выброса. Если подумать, вызов .first() отменяет подписку, может быть, поэтому? - person Per Hornshøj-Schierbeck; 02.08.2017
comment
@ PerHornshøj-Schierbeck, что-то не так с вашей настройкой. Проверьте пример, который я добавил к своему ответу, который показывает, что вы можете подписаться после завершения исходного наблюдаемого - person Max Koretskyi; 02.08.2017
comment
Ваш пример работает со статическим списком [1,2,3], который отправит .done в поток, мой поток никогда не заканчивается, поэтому я думаю, что AsyncSubject не будет работать. Возможно, если бы поток был таймером, вместо of(1,2,3) он лучше имитировал бы мою настройку? Я приму ваш ответ и на этот раз решу его обещаниями: P - person Per Hornshøj-Schierbeck; 02.08.2017
comment
@PerHornshøj-Schierbeck, first завершит трансляцию. Я заменил of на interval - person Max Koretskyi; 02.08.2017
comment
Хорошо, уже слишком поздно - я сделал последнюю попытку и заставил ваш пример работать. Я понятия не имею, что пошло не так в первый раз. Спасибо :) - person Per Hornshøj-Schierbeck; 02.08.2017