我覬覦Promise很久了。但因爲緣分未到,以及智商不夠,一直沒有機會在實戰中用它。前段時間的 2015 上海前端技術峯會 中的主題分享之《ES6實戰》讓我有這樣的感受:ES5的好多特性都尚未徹底掌握呢,ES6就來了。而後聽說明年ES7就要來了……前端
因此說,ajax
學無止境。數據庫
…promise
背景描述
在瀏覽器端,有個表單,爲了填寫方便,兩個輸入框使用<select>元素供用戶選擇;此外,在編輯一個條目時(對應數據庫中一行),還要將該條目的數據從服務器獲取到,而後生成到表單中。瀏覽器
<form id="student-info"> <input type="text" data-key="name"> <select data-key="province"> <option value="10">省份1</option> <option value="11">省份2</option> </select> <select data-key="university"> <option value="1">學校1</option> <option value="2">學校2</option> </select> </form>
幾個工具
沒有使用任何MVC或者MVVM框架。只是藉助於jQuery、Underscore等進行構建。服務器
從表單中獲取數據:框架
/* * selector: 表單容器的選擇器 */ util.getDataFromForm = function(selector){ var data = {}; var inputs = $(selector).find(*[data-key]); inputs.each(function(index, element){ var value = $(element).val(); var key = $(element).data('key'); data[key] = value; }); return data; };
把數據渲染到表單中:異步
/** * data: 模型數據 * selector: 表單容器的選擇器 */ util.renderFormWithData = function(data, selector){ var inputs = $(selector).find(*[data-key]); inputs.each(function(index, element){ var key = $(element).data('key'); $(element).val(data[key]); }); };
最初我是這樣寫Promise代碼的
OK,對於上面這個表單,兩個select元素是須要從服務器GET數據後生成的(藉助於模板,在此不放代碼了)。而後若是要編輯一個條目,還須要額外GET該條目的數據並賦值到表單裏。函數
若是不使用Promise,而是隻用回調寫法,那麼最多的狀況下,須要嵌套三層。任何一層失敗了,都很差辦。而藉助於Promise,能夠大膽地像同步代碼那樣寫:工具
版本-0.0.1
Promise異步流程控制-0.0.1
function prepareForm(callback){ var promise = new Promise(function(resolve, reject) { resolve(); }); promise.then(function(){ $.ajax({ url: '/rest/province/all', type: 'get', success: function(){ /* render the province select element */ } }); }) .then(function(){ $.ajax({ url: '/rest/university/all', type: 'get', success: function(){ /* render the university select element */ } }); }) .then(function(){ if(callback){ callback(); } }) .catch(function(error){ console.log('error: ', error); }); };
在這裏,prepareForm函數依然接受一個回調函數做爲參數。若是是ADD操做,則回調中無須再次執行一個Ajax操做;若是是UPDATE操做,則回調函數須要對該條目進行Ajax請求,而後根據這個值去渲染表單——這時不需擔憂表單裏的兩個select元素還沒有就緒,由於必定是就緒了以後才根據模型對象渲染表單的。
注意:每次調用then都會返回一個新建立的Promise對象。
版本-0.0.2
版本-0.0.1並無很好地處理rejected狀況。
【未完待續】
上面我誤覺得直接鏈式then()方法就是Promise處理異步流程的精髓了。然而大錯特錯。對於異步任務,重要的是保證數據的獲取。這一般就須要自定義一個殼,來包裝一下Ajax請求的參數、返回數據等,尤爲要把返回的數據傳給resolve(),以便在then()裏可使用這個數據。下面是例子。
/* 先定義一個返回Promise對象的Ajax過程 */ var getAjaxPromise = function(option){ return new Promise(function(resolve, reject){ $.ajax({ url: option.url, type: 'get', data: option.data || '', success: function(data){ resolve(data); }, error: function(error){ reject(error); } }); }); }; /* 啓動第1個異步任務 */ var p1 = getAjaxPromise({ url: 'root/url/1' }); p1.then(function(data1){ /* 處理第1個異步任務的結果 */ console.log(data1); /* 而後啓動第2個異步任務 */ return getAjaxPromise({ url: 'root/url/2' }); }) .then(function(data2){ /* 處理第2個異步任務的結果 */ console.log(data2); });
then() 方法能夠鏈式調用,關鍵就是每一個then都會返回一個新的Promise對象。