1. 程式人生 > >Promise使用詳解(轉)

Promise使用詳解(轉)

2015年6月, ES2015(即 ECMAScript 6、ES6) 正式釋出。其中 Promise 被列為正式規範,成為 ES6 中最重要的特性之一。

1.then()方法

簡單來講,then 方法就是把原來的回撥寫法分離出來,在非同步操作執行完後,用鏈式呼叫的方式執行回撥函式。
而 Promise 的優勢就在於這個鏈式呼叫。我們可以在 then 方法中繼續寫 Promise 物件並返回,然後繼續呼叫 then 來進行回撥操作。

(1)下面通過樣例作為演示,我們定義做飯、吃飯、洗碗(cook、eat、wash)這三個方法,它們是層層依賴的關係,下一步的的操作需要使用上一部操作的結果。(這裡使用 setTimeout 模擬非同步操作)

//做飯
function cook(){
	console.log('開始做飯。');
	var p = new Promise(function(resolve, reject){        //做一些非同步操作
		setTimeout(function(){
			console.log('做飯完畢!');
			resolve('雞蛋炒飯');
		}, 1000);
	});
	return p;
}

//吃飯
function eat(data){
	console.log('開始吃飯:' + data);
	var p = new Promise(function(resolve, reject){        //做一些非同步操作
		setTimeout(function(){
			console.log('吃飯完畢!');
			resolve('一塊碗和一雙筷子');
		}, 2000);
	});
	return p;
}

function wash(data){
	console.log('開始洗碗:' + data);
	var p = new Promise(function(resolve, reject){        //做一些非同步操作
		setTimeout(function(){
			console.log('洗碗完畢!');
			resolve('乾淨的碗筷');
		}, 2000);
	});
	return p;
}

(2)使用 then 鏈式呼叫這三個方法:

cook()
.then(function(data){
return eat(data);
})
.then(function(data){
return wash(data);
})
.then(function(data){
console.log(data);
});

當然上面程式碼還可以簡化成如下:

cook()
.then(eat)
.then(wash)
.then(function(data){
	console.log(data);
});

(3)執行結果如下:

2.reject()方法

上面樣例我們通過 resolve 方法把 Promise 的狀態置為完成態(Resolved),這時 then 方法就能捕捉到變化,並執行“成功”情況的回撥。
而 reject 方法就是把 Promise 的狀態置為已失敗(Rejected),這時 then 方法執行“失敗”情況的回撥(then 方法的第二引數)。

(1)下面同樣使用一個樣例做演示

//做飯
function cook(){
	console.log('開始做飯。');
	var p = new Promise(function(resolve, reject){        //做一些非同步操作
		setTimeout(function(){
			console.log('做飯失敗!');
			reject('燒焦的米飯');
		}, 1000);
	});
	return p;
}

//吃飯
function eat(data){
	console.log('開始吃飯:' + data);
	var p = new Promise(function(resolve, reject){        //做一些非同步操作
		setTimeout(function(){
			console.log('吃飯完畢!');
			resolve('一塊碗和一雙筷子');
		}, 2000);
	});
	return p;
}

cook()
.then(eat, function(data){
	console.log(data + '沒法吃!');
})

執行結果如下:
在這裡插入圖片描述
(2)如果我們只要處理失敗的情況可以使用 then(null, …),或是使用接下來要講的 catch 方法。

cook()
.then(null, function(data){
	console.log(data + '沒法吃!');
})

3.catch()方法

(1)它可以和 then 的第二個引數一樣,用來指定 reject 的回撥

cook()
.then(eat)
.catch(function(data){
	console.log(data + '沒法吃!');
});

(2)它的另一個作用是,當執行 resolve 的回撥(也就是上面 then 中的第一個引數)時,如果丟擲異常了(程式碼出錯了),那麼也不會報錯卡死 js,而是會進到這個 catch 方法中。

//做飯
function cook(){
	console.log('開始做飯。');
	var p = new Promise(function(resolve, reject){        //做一些非同步操作
		setTimeout(function(){
			console.log('做飯完畢!');
			resolve('雞蛋炒飯');
		}, 1000);
	});
	return p;
}

//吃飯
function eat(data){
	console.log('開始吃飯:' + data);
	var p = new Promise(function(resolve, reject){        //做一些非同步操作
		setTimeout(function(){
			console.log('吃飯完畢!');
			resolve('一塊碗和一雙筷子');
		}, 2000);
	});
	return p;
}

cook()
.then(function(data){
	throw new Error('米飯被打翻了!');
	eat(data);
})
.catch(function(data){
	console.log(data);
});

執行結果如下:
在這裡插入圖片描述

這種錯誤的捕獲是非常有用的,因為它能夠幫助我們在開發中識別程式碼錯誤。比如,在一個 then() 方法內部的任意地方,我們做了一個 JSON.parse() 操作,如果 JSON 引數不合法那麼它就會丟擲一個同步錯誤。用回撥的話該錯誤就會被吞噬掉,但是用 promises 我們可以輕鬆的在 catch() 方法裡處理掉該錯誤。

(3)還可以新增多個 catch,實現更加精準的異常捕獲。

somePromise.then(function() {
	return a();
}).catch(TypeError, function(e) {
	//If a is defined, will end up here because
	//it is a type error to reference property of undefined
}).catch(ReferenceError, function(e) {
	//Will end up here if a wasn't defined at all
}).catch(function(e) {
	//Generic catch-the rest, error wasn't TypeError nor
	//ReferenceErro;
});

4,all()方法
Promise 的 all 方法提供了並行執行非同步操作的能力,並且在所有非同步操作執行完後才執行回撥。

(1)比如下面程式碼,兩個個非同步操作是並行執行的,等到它們都執行完後才會進到 then 裡面。同時 all 會把所有非同步操作的結果放進一個數組中傳給 then。

//切菜
function cutUp(){
	console.log('開始切菜。');
	var p = new Promise(function(resolve, reject){        //做一些非同步操作
		setTimeout(function(){
			console.log('切菜完畢!');
			resolve('切好的菜');
		}, 1000);
	});
	return p;
}

//燒水
function boil(){
	console.log('開始燒水。');
	var p = new Promise(function(resolve, reject){        //做一些非同步操作
		setTimeout(function(){
			console.log('燒水完畢!');
			resolve('燒好的水');
		}, 1000);
	});
	return p;
}

Promise
.all([cutUp(), boil()])
.then(function(results){
	console.log("準備工作完畢:");
	console.log(results);
});

(2)執行結果如下:
在這裡插入圖片描述

5,race()方法
race 按字面解釋,就是賽跑的意思。race 的用法與 all 一樣,只不過 all 是等所有非同步操作都執行完畢後才執行 then 回撥。而 race 的話只要有一個非同步操作執行完畢,就立刻執行 then 回撥。
注意:其它沒有執行完畢的非同步操作仍然會繼續執行,而不是停止。

(1)這裡我們將上面樣例的 all 改成 race

Promise
.race([cutUp(), boil()])
.then(function(results){
	console.log("準備工作完畢:");
	console.log(results);
});

在這裡插入圖片描述
(2)race 使用場景很多。比如我們可以用 race 給某個非同步請求設定超時時間,並且在超時後執行相應的操作。

//請求某個圖片資源
function requestImg(){
	var p = new Promise(function(resolve, reject){
		var img = new Image();
		img.onload = function(){
			resolve(img);
		}
		img.src = 'xxxxxx';
	});
	return p;
}

//延時函式,用於給請求計時
function timeout(){
	var p = new Promise(function(resolve, reject){
		setTimeout(function(){
			reject('圖片請求超時');
		}, 5000);
	});
	return p;
}

Promise
.race([requestImg(), timeout()])
.then(function(results){
	console.log(results);
})
.catch(function(reason){
	console.log(reason);
});

上面程式碼 requestImg 函式非同步請求一張圖片,timeout 函式是一個延時 5 秒的非同步操作。我們將它們一起放在 race 中賽跑。

  • 如果 5 秒內圖片請求成功那麼便進入 then 方法,執行正常的流程。
  • 如果 5 秒鐘圖片還未成功返回,那麼則進入
    catch,報“圖片請求超時”的資訊。

在這裡插入圖片描述