Promise快速入門

Mopecat發表於2018-03-12

週五就想寫這篇文章,但是無奈花花世界的誘惑太多……就一直拖到了今天,自責1e4遍; 進入正題Promise:

Promise 物件用於表示一個非同步操作的最終狀態(完成或失敗),以及其返回的值。

上為MDNPromise的定義; ES6規定Promise是一個建構函式,用來生成Promise例項(就是跟new一個女朋友一樣); Promise意為‘承諾’,承諾是發生在未來且必須達成的事,對應Promise物件是一個儲存著未來才會結束的非同步操作的“容器”;

Promise物件代表的非同步操作有三個狀態:

  • pending (進行中)
  • fulfilled (已成功)
  • rejected (已失敗) Promise物件有一下兩個特點:
  • 上述的三個狀態一經改變則不會更改且任何時候都可以得到這個結果
  • 只有非同步操作的結果可以決定當前狀態的變化,其他任何操作都不改變Promise物件的狀態,即承諾這個詞的由來,也表現了程式猿的浪漫~~

先說說基本用法然後再來實操~

const promise = new Promise(function(resolve, reject) {
  // ... some code 立即執行

  if (/* 非同步操作成功 */){
    resolve(value);// 將狀態改為fulfilled 並將引數傳遞給then中的回撥函式
  } else {
    reject(error);// 將狀態改為rejected 並將引數傳遞給then/catch中的回撥函式
  }
});
// then方法接受兩個引數分別對應兩個狀態的處理,其中對應rejected的引數可選,兩個引數都接受上面promise物件傳出的引數作為引數
promise.then(function(value) {
  // success 成功處理
}, function(error) {
  // failure 錯誤處理
});
複製程式碼

瞭解了基本用法我們來了解一下Promise到底有啥用啊,我的理解就是以同步的方式(鏈式呼叫)去處理非同步操作(回撥),在這個Promise物件出現之前我們是怎麼處理非同步操作的呢? 舉個栗子~:

console.log('start');
setTimeout(function (name) {
		  var fruitList = name + ',';

		  setTimeout(function (name) {
		    fruitList += name + ',';

		    setTimeout(function (name) {
		      fruitList += name + ',';

		      setTimeout(function (name) {
		        fruitList += name + ',';

		        setTimeout(function (name) {
		          fruitList += name;

		          console.log(fruitList);

		        }, 1, '西瓜');

		      }, 1, '香蕉');

		    }, 1, '橘子');

          }, 1, '蘋果');}, 1, '葡萄');
          
		  console.log('上面是個非同步過程,所以我先出來,後面才是水果');
複製程式碼

我這裡只是簡單的寫了幾層簡單的定時器而已,如果要很多很多層並且稍微複雜一點的回撥函式就很難以維護了且程式碼閱讀很難受。所以Promise的出現就是為了鏈式的呼叫來實現這種非同步操作同樣以本例改為Promise的方式來一遍~:

console.log('start');
var promise1 = new Promise((resolve,reject)=>{
		  	setTimeout(resolve('葡萄'),1);
		  });
promise1.then(value=>new Promise((resolve,reject)=>setTimeout(resolve(value+',蘋果'),1)))
		.then(value=>new Promise((resolve,reject)=>setTimeout(resolve(value+',橘子'),1)))
		.then(value=>new Promise((resolve,reject)=>setTimeout(resolve(value+',香蕉'),1)))
		.then(value=>new Promise((resolve,reject)=>setTimeout(resolve(value+',西瓜'),1)))
		.then(value=>{console.log(value)});

console.log('上面是個非同步過程,所以我先出來,後面才是水果');
複製程式碼

其實本例中的reject引數可以省略,因為我預設他肯定會成功的了,額還有就是寫function比較麻煩這裡直接用了箭頭函式~~恩……在科普一個小坑給你們,如果在vue中使用promise用箭頭函式要比寫function舒服很多,function中this指向的是window而不是vue例項~

再來看看下面這個丟擲錯誤的相關栗子:

console.log("start");
		new Promise((resolve, reject) => {
		    var data = [1,2,3,4,5,6,7];
		    return resolve(data);
		})
		.then((result) => {
		    var newResult = result.slice(1);   
		    return newResult;
		})
		.then((result)=>{
            console.log(result);
			throw new Error('someting error');
		})
		.catch((value)=>{
			console.log(value);// 丟擲錯誤之後 執行catch操作 接受的引數是丟擲的錯誤,其實.catch只是Promise.then(reject)的別名而已
		})
		.then((value) => {
            console.log(value);// undefined 丟擲錯誤後沒有 return 所以這裡是個undefined
		    console.log('我任性,錯了之後仍要執行');// catch之後仍然可以繼續執行then操作 
		});
複製程式碼

註釋上寫的很清楚咯~~

接下來在來個實際點的用法非同步載入圖片(原諒我其實是從阮一峰老師那裡偷來的栗子吃):

function loadImageAsync(url){
		return new Promise((resolve,reject)=>{
		var image = new Image();
		image.onload = function(){
			resolve(image);
		}
		image.onerror = function(){
			reject(new Error('不能載入圖片,url是'+ url));
		}
		image.src = url;
	});
}
loadImageAsync('QQ圖片20171114220539.png').then(value =>{
	console.log(value);
	document.body.append(value);
});
複製程式碼

由於最近確實是忙得頭昏腦脹,就先簡單的分享一波基礎用法,剩下的關於promise的兩種模式Promise.all()Promise.race()等我改天再加或者另發一篇吧 同樣你也可以去這裡學習瞭解一下:http://es6.ruanyifeng.com/#docs/promise#Promise-race