在傳統的解決方案中,js實現異步編程採用的方法是回調函數和事件監聽(事件發佈訂閱),可是當應用很複雜很龐大時,大量的回調會讓調試程序變得舉步維艱,成爲開發者的噩夢。php
promise是在es6標準中的一種用於解決異步編程的解決方案,因爲在語言級別上,不一樣於Java、Python等多線程語言,js是單線程的,因此在node.js中大量使用了異步編程的技術,這樣作是爲了不同步阻塞。
node
promise意爲承諾,擬定一個承諾,當承諾實現時即返回結果,不受其餘操做的影響,能夠把它理解爲一個簡單的容器,裏面存放着一個未來會結束的事件返回結果(即異步操做)。不一樣於傳統的回調函數,在promise中,全部的異步操做的結果均可以經過統一的方法處理。promise有三種狀態:es6
pending(進行中),resolved(成功),rejected(失敗),異步操做的結果決定了當前爲哪種狀態,promise的狀態只有兩種改變狀況,且僅改變一次:由pending轉變爲resolved,由pending轉變爲rejected,結果將會保持不變。web
下面代碼是一個簡單的promise實例:編程
const promise = new Promise(function(resolve, reject){
//some code
if(/*異步操做成功*/){
resolve(value);
} else {
reject(error);
}
});複製代碼
Promise
構造函數接受一個函數做爲參數,該函數的兩個參數分別是resolve
和reject
。它們是兩個函數。promise實例生成後,能夠用then
方法分別指定resolved
和rejected
的回調函數。其中第二個函數是可選的,下面是一個Promise對象的簡單例子:
json
function timeout(ms){
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, "done");
});
}
timeout(100).then((value) => {
console.log(value);
});複製代碼
timeout方法返回一個Promise實例,表示一段時間之後纔會發生的結果,過了指定的時間後,Promise狀態變爲resolved,觸發then方法綁定的回調函數。api
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方法指定的回調函數,將在當前腳本全部同步任務執行完纔會執行,因此resolved最後輸出。數組
以上簡單介紹了promise的基本特性:一旦建立當即執行;三種狀態:執行中,成功,失敗;結果不受其餘操做影響,結果不可取消;當異步操做完成或失敗時,Promise會處理一個單個事件。promise
Observable便可觀察對象,在不少軟件編程任務中,或多或少你都會指望你寫的代碼能按照編寫的順序,一次一個的順序執行和完成。可是在ReactiveX(基於一系列可觀察的異步和基礎事件編程組成的一個庫)中,不少指令多是並行執行的,以後他們的執行結果纔會被觀察者捕獲,順序是不肯定的。爲達到這個目的,你定義一種獲取和變換數據的機制,而不是調用一個方法。在這種機制下,存在一個可觀察對象(Observable),觀察者(Observer)訂閱(Subscribe)它,當數據就緒時,以前定義的機制就會分發數據給一直處於等待狀態的觀察者哨兵。bash
ReactiveX 可見模式容許你使用數組等數據項的集合來進行些異步事件流組合操做。它使你從繁瑣的web式回調中解脫,從而能使得代碼可讀性大大提升,同時減小bug的產生。
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)); 複製代碼
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); 複製代碼
下面再經過一個angular2實例場景瞭解promise和observable在實際應用中的區別:
首先,咱們來定義一下問題的場景。假設咱們要實現一個搜索功能,有一個簡單的輸入框,當用戶輸入文字的時候,實時的利用輸入的文字進行查詢,並顯示查詢的結果。
在這個簡單的場景當中,通常須要考慮3個問題:
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<Response>的對象,咱們把返回的結果從Observable<Response> 轉換成 Promise<Response>對象,而後使用它的then方法把結果轉成json。這樣,這個search方法的返回類型爲Promise<Array<string>>。這樣雖然查詢功能基本實現了,可是前面提到的三個問題都沒有解決。
下面應用observable實現功能:
(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));
}
}複製代碼
這裏的this.term.valueChanges是一個Observable<string>
對象,經過debounceTime(400)咱們設置它的事件觸發延時是400毫秒。這個方法仍是返回一個Observable<string>對象。而後咱們就給這個對象添加一個訂閱事件
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));複製代碼
上面的代碼解決了第二個問題,就是通過400ms的延時之後,用戶輸入的搜索條件同樣的狀況。Observable有一個distinctUntilChanged的方法,他會判斷從消息源過來的新數據跟上次的數據是否一致,只有不一致纔會觸發訂閱的方法。
(3)處理返回順序
上面描述了服務器端異步返回數據的時候,返回順序不一致出現的問題。對於這個問題,咱們的解決辦法就比較直接,也就是對於以前的請求返回的結果,直接忽略,只處理在頁面上用戶最後一次發起的請求的結果。咱們能夠利用Observable
的dispose()
方法來解決。實際上,咱們是利用這種’disposable’特性來解決,而不是直接調用dispose()
方法。(實在不知道該怎麼翻譯’disposable’,它的意思是我能夠停止在Observable對象上的消息處理,字面的意思是可被丟棄的、一次性的。)
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的特性去丟棄上一個未及時返回的結果。
總結:在處理某些複雜異步應用中,observable比promise更受開發者青睞,由於使用Observable建立的異步任務,能夠被處理,並且是延時加載的。而promise設計的初衷只是爲了解決大量的異步回調所形成的難以調試問題,observable封裝了大量的方法供咱們使用以處理複雜的異步任務。
以上內容部分摘自阮一峯阮老師的《es6入門》,關於promise和observable的區別,還有一個較好的視頻:egghead.io上的這個7分鐘的視頻(做者Ben Lesh)。第一次在掘金上寫文章,算是對我的知識的總結,不免會有錯漏,歡迎你們指點。