我觊觎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对象。