Promise應用場景

衆所周知,Promise是爲了解決回調地獄問題而產生的,其主要應用於向發服務端發起請求等異步操。那麼,除了異步請求還有其餘場景適合使用 Promise 嗎?下面咱們來共同探討下。javascript

併發執行的任務

Promise除了用於異步操做,還能夠用來記錄一個業務流程的狀態變化,俗稱「狀態機」。咱們可使用 Promise 提供的 Promise.all 方法來實現並行執行任務。好比如今有一個表單頁,裏面有若干多個表單項,只有在全部表單都經過校驗才容許用戶進行下一步操做。下面咱們來提煉關鍵要素:java

  • 校驗表單是併發操做(這裏不考慮關聯表單)
  • 只有全部表單都校驗經過才容許下一步操做

當看到這樣的業務邏輯時,是否是首先想到的是設置一個 flag 變量,而後經過改變flag變量來表示全部表單是否校驗經過,這是最容易想到的實現思路。promise

let flag = true;
let fields = [];

fields.forEach(field=>{
    if(!field.validate()) {
  	    flag = false;//校驗不經過
    }
})
複製代碼

咱們如今來考慮下這麼作有沒有問題?咱們保存了多餘的變量,而且這樣的代碼是不支持遠程服務端校驗的,若是要支持異步操做須要增長更多的代碼量,並且這樣的代碼也是難以理解和維護的。這個時候咱們不妨轉換下思路,將每一個表單項校驗看作是一個 Promise 狀態機,當校驗經過, Promise 狀態爲 fullfilled,校驗不經過爲 rejected。如今咱們使用 Promise 重構上面的代碼,使每個表單校驗都支持異步操做,而且更加容易理解。併發

咱們能夠利用 Promise.all的特性,只有在全部表單項狀態都爲fullfilled狀況下,才容許執行Promise.all後面的then方法app

let p1 = new Promise(function(resolve,rejected) {
	//表單1校驗經過
  if(field.validate()) {
  	resolve()
  } else {
  	rejected()
  }
})
//p2,p3...
Promise.all([p1,p2,p3...]).then(()=>{
	//全部表單項都校驗經過
})
複製代碼

順序執行的任務

有時候咱們會遇到有一系列任務順序執行的狀況。舉個例子,如今有一個購物車的需求,用戶必須先下單以後才能進行付款,而後付款以後才能夠跳轉到訂單頁面,這個需求就能夠用 Promise 順序執行的邏輯實現,狀態流轉是這樣的:異步

咱們來提煉下關鍵需求點:

  • 每個業務環節須要順序執行,執行順序不可變,狀態不可逆
  • 上一個執行結果可能須要做爲下一步的輸入

實現這樣的業務邏輯咱們一樣能夠不使用 Promise,可是可能實現過程較複雜。咱們知道,系統越是複雜,出錯的機率越高,因此咱們必須用盡可能簡單的方法實現以上需求。這裏咱們將用到 Promise 的順序執行。
實現Promise順序執行需求如下兩個步驟:async

  • 定義一個空的 resolved promise,這個promise只是做爲創建鏈的起點
  • 在一個循環中,經過鏈中上一個 promise 調用 then()得到新的 promise,更新promise 變量
//利用forEach實現
function run(tasks=[]) {
  let promise = Promise.resolve();
  tasks.forEach(task=>{
    promise = promise.then((value)=>{
      return task(value);
    })
  })
  return promise;
}
複製代碼

//利於reduce實現
function run(tasks=[]) {
  let promise = tasks.reduce((prev,task)=>{
    return prev.then((value)=>{
      return task(value);
    })
  },Promise.resolve());
	return promise
}
複製代碼

兼容callback模式

固然,Promise並非萬金油,不是全部的場合都適合用 Promise。若是是較爲簡單的邏輯,使用 callback 會更加方便。並且若是咱們是在寫一個不少人都在用的公共庫的話,有些開發者可能不喜歡使用Promise或者對 Promise 不熟悉。這時,咱們須要提供一種解決方案,使咱們的API能夠同時兼容 Promise 和 callback 兩種模式。提供一個回調API,回調參數可選,通常狀況下使用 callback,在 callback 未傳遞的狀況下使用 Promise。咱們能夠經過高階函數實現此功能:函數

//將返回promise的函數轉化爲既返回promise又支持
//callback的函數,調用時最後一個參數爲callback
function ptc (fn) {
	if(typeof fn !== 'function') {
		throw new Error("缺乏必要參數");
	}
	return function(...args) {
		let callback = args[args.length-1];
		let _args = args.slice(0,args.length-1);
		return fn.apply(this,args).then(res=>{
			if(typeof callback == 'function') {
				callback.call(this,null,res)
			}
			return res;
		}).catch(e=>{
			if(typeof callback == 'function') {
				callback.call(this,e,null)
			}
			return e;
		})
	}
}

//測試
a= function(x) {return Promise.resolve(x)}

b=pc(a)

b('xxx',function(err,data){
    console.log(err,data)//err:null,data:'xxx'
})


b('asss').then(res=>{
	console.log(res);
})

複製代碼

總結

Promise 實例能夠看作是一個狀態機,任何具備狀態和狀態改變的業務流程均可以使用 Promise 來實現,在結合了 ES7 的 async function特性後功能更增強大。這裏咱們只是列舉了兩個很簡單的小例子來幫助你們理解,更多複雜的應用場景等待你們去細心觀察和總結,共勉!測試

相關文章
相關標籤/搜索