js中的promise和then

許正陽發表於2019-01-13

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>promise學習</title>
		<!-- 在js中,所有程式碼都是單執行緒執行的 -->
		<!-- 非同步執行:可以使用回撥,在ES6中我們可以使用Promise物件來實現 -->
		<!-- 
		 1.1Promise物件,只需要then一個方法,then方法帶有如下三個引數
		     1.成功回撥
		     2.失敗回撥
		     3.前進回撥(暫時不講)
		     一個全新的 promise 物件從每個 then 的呼叫中返回。
		 1.2 Promise物件代表一個非同步操作,其不受外界影響,有三種狀態
			.Pending進行中
			.Resolved(已完成,又稱Fulfilled)
			.Rejected(已失敗)
		 1.3使用Promise的優勢
			1.3.1 解決回撥地獄問題(Callback Hell)
				例如,有時候我們可能會進行多個非同步操作,後一個的請求需要上一次請求的返回結果,所以過去我們都是用callback層層巢狀,
				但是多了的話就會出現回撥地獄,程式碼的可讀性和維護性都會變得很差
				firstAsync(function(data){
                        //處理得到的 data 資料
						//....
						secondAsync(function(data2){
							//處理得到的 data2 資料
							//....
							thirdAsync(function(data3){
								  //處理得到的 data3 資料
								  //....
							});
					});
				});
			1.3.2 使用promise的話,程式碼會變得扁平和可讀.前面提到了then返回一個promise,因此我們可以將then的呼叫不停的串聯起來,其中then返回的
			promise裝載了由呼叫返回的值.
				firstAsync()
				.then(function(data){
					//處理得到的 data 資料
					//....
					return secondAsync();
				})
				.then(function(data2){
					//處理得到的 data2 資料
					//....
					return thirdAsync();
				})
				.then(function(data3){
					//處理得到的 data3 資料
					//....
				});
			1.3.3 更好的進行錯誤捕獲
				多層巢狀會造成無法捕獲異常,使用promise,通過使用reject方法把promise的狀態設定為rejected,這樣我們在then中就能捕捉到,然後執行失敗情況的回撥
					function fetch(callback) {
						return new Promise((resolve, reject) => {
						setTimeout(() => {
								reject('請求失敗');
							}, 2000)
						})				
					}
					fetch()
						.then(
							function(data){
								console.log('請求處理');
								console.log(data);
							},
							function(reason, data){
								console.log('觸發異常');
								console.log(reason);
							}
						);
				當然我們在catch方法中處理reject回撥也是可以的,
				function fetch(callback) {
					return new Promise((resolve, reject) => {
						setTimeout(() => {
							 reject('請求失敗');
						}, 2000)
					})
				}
				fetch()
					.then(
						function(data){
							console.log('請求處理');
							console.log(data);
						}
					)
					.catch(function(reason){
						console.log('觸發異常');
						console.log(reason);
					});
					
		 -->
	</head>
	<body>
		<script type="text/javascript">
						new Promise(function(resolve, reject) {
							console.log('start new Promise...');
							var timeOut = Math.random() * 2;
							console.log('set timeout to: ' + timeOut + ' seconds.');
							setTimeout(function() {
								if (timeOut < 10) {
									console.log('call resolve()...');
									resolve('200 OK');
								} else {
									console.log('call reject()...');
									reject('timeout in ' + timeOut + ' seconds.');
								}
							}, timeOut * 1000);
						}).then(function(r) {
							console.log('Done: ' + r);
						}).catch(function(reason) {
							console.log('Failed: ' + reason);
						});
// 			promise的使用詳解,簡單來講,then方法就是把原來的回撥寫法分離出來,在非同步執行操作執行完成後,用鏈式呼叫的方式,回撥函式;
// 			我們可以在then方法中繼續寫promise物件並返回,然後繼續呼叫then進行回撥操作

			// then方法舉例          例如 學習 考試  放假
			function study() {
				var p = new Promise(function(resolve, reject) {
					setTimeout(() => {
						resolve("學習結束,開始考試")
					}, 2000)
				});
				return p;
			}

			function test(data) {
				var p = new Promise(function(resolve, reject) {
					setTimeout(() => {
						resolve("考試結束,開始假期")
					}, 2000)
				});
				return p;
			}

			function holiday(data) {
				var p = new Promise(function(resolve, reject) {
					setTimeout(() => {
						resolve("假期結束,開始上課")
					}, 2000)
				});
				return p;
			}
			// 使用then鏈式呼叫這三個方法
			study()
				.then(function(data) {
					console.log(data)
					return test(data);
				})
				.then(function(data) {
					console.log(data)
					return holiday(data)
				})
				.then(function(data) {
					console.log(data)
				})
			// 執行結果
			// 學習結束,開始考試
			// 試結束,開始假期
			// 假期結束,開始上課



			// 2.reject方法
			function learn() {
				var p = new Promise(function(reslove, reject) {
					setTimeout(() => {
						reject("考試不及格")
					}, 1000)
				});
				return p;
			}
			learn()
				.then(test, function(data) {
					console.log(data + "無法放假,複習吧");
				})
			// 執行結果
			// 考試不及格無法放假,複習吧
			// 另外如果我們只要處理失敗的情況,可以使用then(null,.....),或者使用catch方法
			learn()
				.then(null, function(data) {
					console.log(data + "無法放假,複習吧");
				})



			// catch方法和then方法的第二個引數一樣,用來指定reject的回撥
			learn()
				.then(test)
				.catch(function(data) {
					console.log("沒得玩了");
				})
			// 另一個作用是,當執行resolve的回撥時,如果丟擲了異常(程式碼出錯),那麼也不會報錯卡死js,而是會進到這個catch中
			study()
				.then(function(data) {
					throw new Error("考題洩漏");
					test(data)
				})
				.catch(function(data) {
					console.log(data + "無法繼續考試")
				})





			// all方法,提供了並行非同步執行操作的能力,並且在所有非同步操作執行完後才會執行回撥
			// 例如放假要等到學習和考試之後
			setTimeout(() => {
				Promise.all([study(), test(),holiday()])
					.then(function(data) {
						console.log("開始放假了:後面的是data資料   "+data.length+"第一個:"+data[0]+"第二個:"+data[1]+"第三個:"+data[2])
						// 列印結果:   開始放假了:後面的是data資料   3第一個:學習結束,開始考試第二個:考試結束,開始假期第三個:假期結束,開始上課
					})
			}, 1000)
			// race方法,用法與all一樣,只是all是等所有的非同步操作完成之後,才會執行then回撥.而race回撥的話只要有一個非同步操作執行完畢,就立刻執行then回撥;
			//then方法裡面的回撥中的引數data是一個陣列,我們可以得到非同步執行操作的結果
			// PS:其他沒有執行完畢的非同步操作,仍然會繼續執行,而不是停止
			setTimeout(() => {
				Promise.race([study(), test()])
					.then(function(data) {
						console.log("考試不考試,學習不學習,都沒關係的")
					})
			}, 20000)
			// race的使用場景很多,例如我們可以給某一個race設定請求超時時間
			//考研開始,5s內交卷認為合格,否則認為不合格
			function passTheExam() {
				var p = new Promise(function(resolve, reject) {
					setTimeout(() => {
						resolve("交卷")
					}, 25000);
				})
				return p;
			}

			function requestTimeOut() {
				var p = new Promise(function(resolve, reject) {
					setTimeout(() => {
						reject("考試失敗")
					}, 30000);
				})
				return p;
			}
			Promise.race([passTheExam(),requestTimeOut()])
			.then(function(data){
				console.log(data);
			})
			.catch(function(err){
				console.log(err);
			})
		</script>
	</body>
</html>
複製程式碼


相關文章