Promise vs Observable (небольшие заметки js)

внешний интерфейс сервер JavaScript Promise

1.promise

В традиционном решении методы, используемые js для реализации асинхронного программирования, — это функции обратного вызова и мониторинг событий (публикация и подписка на события), но когда приложение очень сложное и большое, большое количество обратных вызовов затруднит отладку программы. и стать любимым кошмаром разработчика.

Promise — это решение для асинхронного программирования в стандарте es6.Поскольку на уровне языка, в отличие от многопоточных языков, таких как Java и Python, js является однопоточным, поэтому в node.js широко используется асинхронный метод программирования для сделайте это, чтобы избежать синхронной блокировки.

Обещание означает обещание, сформулировать обещание, когда обещание будет выполнено, результат будет возвращен, и на него не повлияют другие операции.Это можно понимать как простой контейнер, в котором хранится возвращаемый результат события, которое завершится в будущем (т. е. асинхронная операция). В отличие от традиционных функций обратного вызова, в промисах результаты всех асинхронных операций могут обрабатываться унифицированным образом. Промис имеет три состояния:

ожидание (выполняется), разрешено (успех), отклонено (сбой), результат асинхронной операции определяет, в каком состоянии он находится в данный момент, изменений состояния промиса всего два, и только одно изменение: с ожидания на разрешено, ожидая отклонения, результат останется прежним.

Следующий код является простым примером обещания:

const promise = new Promise(function(resolve, reject){
//some code
if(/*异步操作成功*/){
    resolve(value);
} else {
    reject(error);
}
});

PromiseКонструктор принимает функцию в качестве параметра, два параметра функцииresolveа такжеreject. Это две функции. После создания экземпляра обещания вы можете использоватьthenспособы оговариваются отдельноresolvedа такжеrejectedфункция обратного вызова. Если вторая функция необязательна, ниже приведен простой пример объекта Promise:

function timeout(ms){
    return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, "done");
    });
}

timeout(100).then((value) => {
    console.log(value);
});

Метод timeout возвращает экземпляр Promise, указывающий результат, который произойдет через определенный период времени.По истечении указанного времени состояние Promise разрешается, вызывая функцию обратного вызова, связанную методом then.

let promise = new Promise(function(resolve, reject){
    console.log("Promise");
    resolve();
});

promise.then(function(){
    console.log("resolved.");
});
console.log("Hi!");
// Promise
// Hi!
// resolved.

В приведенном выше коде Promise выполняется сразу после его создания, поэтому Promise возвращается первым, а затем функция обратного вызова, указанная в методе then, будет выполняться после выполнения всех задач синхронизации текущего скрипта, поэтому в конечном итоге будет выведено разрешение.

Больше, чем обещание краткого введенияОсновные характеристики: выполнить сразу после создания; три состояния: выполняется, успешно, не удалось; на результат не влияют другие операции, и результат не может быть отменен; когда асинхронная операция завершается или завершается с ошибкой, Promise обрабатывает одно событие.

2.Observable

Наблюдаемые объекты — это наблюдаемые объекты, и во многих задачах программирования вы более или менее ожидаете, что код, который вы пишете, будет выполняться и завершаться в том порядке, в котором вы его написали, по одному за раз. Но в ReactiveX (библиотеке, основанной на ряде наблюдаемых асинхронных и базовых событийных программ) многие инструкции могут выполняться параллельно, и тогда результаты их выполнения будут захвачены наблюдателем, а порядок неопределен. Для этого вы определяете механизм получения и преобразования данных, а не вызов метода. В соответствии с этим механизмом есть наблюдаемый объект (Observable), наблюдатель (Observer) подписывается (Subscribe) на него, когда данные будут готовы, ранее определенный механизм будет распространять данные на часовой наблюдатель, который находился в состоянии ожидания.

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

  • Наблюдаемые данные очень гибкие, в отличие от промисов, которые могут обрабатывать только одно значение, observalbe поддерживает несколько значений и даже потоки данных.
  • Когда создаются observalbes, они не выполняются сразу (ленивая оценка), а вызываются только тогда, когда результат действительно нужен. Например, в следующем коде для промисов, вызовут они потом или нет, будут выполняться промисы, тогда как observables только создаются и не будут выполняться, а только тогда, когда результат действительно нужен, например foreach здесь, будет выполнен .

var promise = new Promise((resolve) => {      setTimeout(() => {          resolve(42);      }, 500);      console.log("promise started");  });    //promise.then(x => console.log(x));    var source = Rx.Observable.create((observe) => {      setTimeout(() => {          observe.onNext(42);      }, 500);      console.log("observable started");  });    //source.forEach(x => console.log(x));  

  • Observables можно отменить (dispose), observables можно отменить до или во время выполнения, то есть отписаться. В приведенном ниже примере наблюдаемый объект закрывается через 0,5 секунды, поэтому журнал «наблюдаемый тайм-аут» не будет напечатан.

var promise = new Promise((resolve) => {      setTimeout(() => {          console.log("promise timeout hit")          resolve(42);      }, 1000);      console.log("promise started");  });    promise.then(x => console.log(x));    var source = Rx.Observable.create((observe) => {      id = setTimeout(() => {          console.log("observable timeout hit")          observe.onNext(42);      }, 1000);      console.log("observable started");        return () => {          console.log("dispose called");          clearTimeout(id);      }  });    var disposable = source.forEach(x => console.log(x));    setTimeout(() => {      disposable.dispose();  }, 500);  

  • Наблюдаемые объекты можно вызывать несколько раз (повторять).Результат, возвращаемый наблюдаемым объектом, может вызываться и обрабатываться несколько раз, что может запускать несколько асинхронных операций.Многие методы инструментов инкапсулированы в наблюдаемые для управления наблюдаемыми результатами и их объединения. , трансформировать. В приведенном выше коде вы можете получить переменные обещания и наблюдаемые. Для промисов, независимо от того, как потом будет вызываться then, фактическая асинхронная операция будет выполнена только один раз, и множественные вызовы не будут иметь никакого эффекта; но для наблюдаемых многократные вызовы forEach или использование метода retry могут инициировать несколько асинхронных операций.

Давайте воспользуемся сценарием экземпляра angular2, чтобы понять разницу между промисами и наблюдаемыми в практических приложениях:

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

В этом простом сценарии обычно необходимо учитывать три вопроса:

  • Вы не можете запускать поиск каждый раз, когда пользователь вводит каждый символ.
    Если пользователь вводит каждый символ для запуска поиска, ресурсы сервера тратятся впустую, а клиент часто запускает поиск и обновляет результаты поиска, что также повлияет на ответ клиента. Как правило, этой проблемы можно избежать, добавив некоторые задержки.
  • Если введенный пользователем текст не изменился, поиск повторять не следует.
    Предположим, что пользователь вводит «foo», делает небольшую паузу, запускает поиск, затем набирает символ «o», обнаруживает, что опечатка неверна, и удаляет символ. Если в это время пользователь делает паузу, что запускает поиск, текст «foo» на этот раз совпадает с текстом в предыдущем поиске, поэтому его не следует искать снова.
  • Рассмотрим асинхронный возврат сервера.
    Когда мы отправляем несколько запросов на сервер асинхронно, нам нужно обратить внимание на порядок, в котором полученные возвраты не гарантируются. Например, мы последовательно искали два слова «компьютер» и «автомобиль», хотя слово «автомобиль» искалось позже, возможно, сервер сможет быстрее обработать этот поиск и вернуть результат первым. Таким образом, на странице сначала будут отображаться результаты поиска по запросу "автомобиль", а затем, когда будут получены результаты поиска по запросу "компьютер", будут отображаться результаты по запросу "компьютер". Однако в это время пользователю кажется, что поиск «автомобиль» очевиден, но отображается другой результат.
Во-первых, это начальная версия обещания:

import { Injectable } from '@angular/core';
import { URLSearchParams, Jsonp } from '@angular/http';
@Injectable()
export class WikipediaService {  

constructor(private jsonp: Jsonp) {}  

search (term: string) {    
    var search = new URLSearchParams()    
    search.set('action', 'opensearch');    
    search.set('search', term);    
    search.set('format', 'json');    
    return this.jsonp                
    .get('http://en.wikipedia.org/w/api.php?callback=JSONP_CALLBACK', { search })       
    .toPromise()             
    .then((response) => response.json()[1]);  
            }
        }

В приведенном выше коде модуль Jsonp используется для запроса результата API, и его результатом должен быть объект типа Observable.Мы преобразуем возвращенный результат из Observable в объект Promise, а затем используйте его Метод then преобразует результат в json. Таким образом, возвращаемый тип этого метода поиска — Promise>. Таким образом, хотя функция запроса в основном реализована, три упомянутые выше проблемы не решены.

Следующее наблюдаемое приложение реализует функцию:

(1) Управление задержкой пользовательского ввода

export class AppComponent { 
      items: Array<string>;  
      term = new FormControl();  
      constructor(private wikipediaService: WikipediaService) { 
      this.term.valueChanges        
      .debounceTime(400)          
      .subscribe(term => this.wikipediaService.search(term)
      .then(items => this.items = items));  
        }
    }

Вот itherm.valuechanges - наблюдаемый >Объект, через DebounceTime (400) мы устанавливаем задержку триггера событий до 400 миллисекунд. Этот метод все еще возвращает наблюдаемый объект . Затем мы добавляем мероприятие подписки на этот объект

subscribe(term => this.wikipediaService.search(term).then(items => this.items = items));

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

(2) Предотвратить двойное срабатывание

this.term.valueChanges           .debounceTime(400)           .distinctUntilChanged()           .subscribe(term => this.wikipediaService.search(term).then(items => this.items = items));

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

(3) Обработка Обратный заказ

Выше описана проблема непоследовательного порядка возврата, когда сервер возвращает данные асинхронно. Для этой проблемы наше решение относительно простое, то есть результат, возвращенный предыдущим запросом, игнорируется, и обрабатывается только результат последнего запроса, инициированного пользователем на странице. мы можем использоватьObservableизdispose()метод решения. На самом деле, мы используем эту характеристику этого «одноразовой», чтобы решить, а не напрямую звонитьdispose()метод. (Я действительно не знаю, как переводить «одноразовые», это означает, что я могу прервать обработку сообщений на наблюдаемый объект, который буквально означает одноразовые, одноразовые.)

search (term: string) {
  var search = new URLSearchParams()
  search.set('action', 'opensearch');
  search.set('search', term);
  search.set('format', 'json');
  return this.jsonp             
 .get('http://en.wikipedia.org/w/api.php?callback=JSONP_CALLBACK', { search })              
 .map((response) => response.json()[1]);
}

Обратите внимание, что этот метод, наконец, использует .map((response) => response.json()[1]), что означает, что исходный результат типа Response преобразуется в список фактических результатов поиска, то есть используется функция Observable. чтобы отбросить верхний результат A, который не был возвращен вовремя.

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

Приведенный выше контент частично взят из «Введения в es6» Руана Ифэн Руана о разнице между промисами и наблюдаемыми, также есть видео получше: это на egghead.io7 минут видео(авторBen Lesh). Я впервые пишу статью о Наггетсах.Это резюме моих личных знаний.Ошибки и упущения неизбежны.Все желающие могут подсказать.