有时候,你会发现满世界都是某个字眼。
Promise,就是这个家伙。简直阴魂不散,走到哪儿都能看到它。fetch
、koa
中都有TA的身影,而我却对TA一无所知。
搜索TA的文章介绍,第一页中最早的一篇文章已经是2011年了。那是个我还不会HelloWorld
的年代啊!
用法
Promise 对象用于延迟(deferred) 计算和异步(asynchronous ) 计算。一个Promise对象代表着一个还未完成,但预期将来会完成的操作。
看概念很迷糊,直接看实例理解吧。
function $http(url){ var core = { ajax : function (method, url, args) { var promise = new Promise( function (resolve, reject) { var client = new XMLHttpRequest(); var uri = url; if (args && (method === 'POST' || method === 'PUT')) { uri += '?'; var argcount = 0; for (var key in args) { if (args.hasOwnProperty(key)) { if (argcount++) { uri += '&'; } uri += encodeURIComponent(key) + '=' + encodeURIComponent(args[key]); } } } client.open(method, uri); client.send(); client.onload = function () { if (this.status >= 200 && this.status < 300) { resolve(this.response); } else { reject(this.statusText); } }; client.onerror = function () { reject(this.statusText); }; }); return promise; } }; return { get : function(args) { return core.ajax('GET', url, args); }, post : function(args) { return core.ajax('POST', url, args); }, put : function(args) { return core.ajax('PUT', url, args); }, delete : function(args) { return core.ajax('DELETE', url, args); } }; }; $http('http://url/api').get({ id: 111 }).then(function(data){ }, function(data){ });
|
以上是一个基于 Promise 的 ajax 实现,调用形式跟 vue-resource 如出一辙(果然又是大神们嚼烂的玩意儿)。
Promise 的构造函数可以带有1个参数,它是带有resolve
、reject
两个参数的函数对象,如以上core.ajax
函数中创建的 Promise 对象。
其中 resolve 用于处理执行成功的场景,reject 用于处理执行失败的场景。
在成功与失败的处理阶段 hook 到一个 Promise 对象中,最后通过then
方法来真正的处理返回结果。
Promise.prototype.then(onFulfilled[, onRejected])
Promise.prototype.catch(onRejected)
并不是所有的失败场景都需要在then
方法中处理,在其后继续追加catch
方法也是可以的。
$http('http://url/api').get({ id: 111 }).then(function(data){ }).catch(function(data){ });
|
由于then
和catch
仍然返回一个 Promise 对象,所以可以出现多个then
或catch
来处理不同的业务场景。
$http('http://url/api').get({ id: 111 }).then(function(data){ return handleA(data); }).then(function(data){ return handleB(data); });
|
为何要用
咋一看 Promise 就是一个包装好的代理对象。不过它的出现究竟是为了解决什么问题呢?
在 Promise 出现之前,如果你的逻辑中出现了异步操作,而又需要在异步操作外获取异步操作内的结果,不外乎是要使用回调了。
function getList(callback) { $.get(yourApi, function(data){ callback(data); }); }
|
使用 Promise 呢?
function getList() { return new Promise( function (resolve, reject) { $.get(yourApi, function(data){ resolve(data); }).error(function(data){ reject(data); }); } }
|
使用 Promise 代理了原来所需要的回调的,返回一个对象而不是传入回调参数的方法形式更直观和容易理解。
在之后的编程中,当目标方法返回了一个 Promise 对象,我们就知道发生了异步操作,需要通过 then 方法来处理场景需求。
到此为止,Promise 给我的感觉也只不过是语法糖罢了,这种形式的写法我也可以封装出来啊。
接下来才是其真正的优点。
比如异步处理多个的请求结果,原始写法。
function getData(callback){ $.get('/ApiA', function(dataA){ $.get('/ApiB', function(dataB){ $.get('/ApiC', function(dataC){ callback(dataA, dataB, dataC); }); }); }); }
|
当然以上是一种比较蠢的写法,当请求数量发生变化时的时候,这段代码就需要修改了。
可以用递归优化下。
function getData(url, callback){ if(url instanceof Array) { var i = 0, len = url.length, dataArr = [], eachLoad = function(url){ $.get(url, function(data){ dataArr.push(data); if(i++ < len - 1) { eachLoad(url[i]); } else { callback.apply(null, dataArr); } }); }; eachLoad(url[i]); } else { $.get(url, function(data){ callback(data); }); } }
|
使用 Promise 解决。
var promiseA = $http('/List').get({ id: 111 }), promiseB = $http('/List').get({ id: 222 }); Promise.all([promiseA, promiseB]).then(function(data){ });
|
使用Promise.all
方法一次性处理多个操作。
Promise 属于 ES6 特性,目前在 IE 中还不能用,不过各大框架也都有自己的实现。
jQuery 中的 Promise
jQuery 中很多异步操作都会返回一个延迟对象$.Deferred
,该对象具有一系列的操作方法。
$('div').animate({ width: '50%' }, 300).done(function(){ }); $.get('/List').then(function(){ }, function(){ }); $.when($.ajax('/List1'), $.ajax('/List2')).then(function(){ }, function(){ });
|
参考