这里,then做了简化,其他promise类库的实现比这个要复杂得多,同时功能也更多,比如还有第三个参数——notify,表示promise当前的进度,这在设计文件上传等时很有用。对then的各种参数的处理是最复杂的部分,有兴趣的同学可以参看其他类Promise库的实现。
在then的基础上,应该还需要至少两个方法,分别是完成promise的状态从pending到resolved或rejected的转换,同时执行相应的回调队列,即resolve()和reject()方法。
到此,一个简单的promise就设计完成了,下面简单实现下两个promise化的函数:
function sleep(ms) { return function(v) { var p = Promise(); setTimeout(function() { p.resolve(v); }, ms); return p; }; }; function getImg(url) { var p = Promise(); var img = new Image(); img.onload = function() { p.resolve(this); }; img.onerror = function(err) { p.reject(err); }; img.url = url; return p; };由于Promise构造函数接受一个异步任务作为参数,所以getImg还可以这样调用:
function getImg(url) { return Promise(function(resolve, reject) { var img = new Image(); img.onload = function() { resolve(this); }; img.onerror = function(err) { reject(err); }; img.url = url; }); };接下来(见证奇迹的时刻),假设有一个BT的需求要这么实现:异步获取一个json配置,解析json数据拿到里边的图片,然后按顺序队列加载图片,没张图片加载时给个loading效果
function addImg(img) { $('#list').find('> li:last-child').html('').append(img); }; function prepend() { $('<li>') .html('loading...') .appendTo($('#list')); }; function run() { $('#done').hide(); getData('map.json') .then(function(data) { $('h4').html(data.name); return data.list.reduce(function(promise, item) { return promise .then(prepend) .then(sleep(1000)) .then(function() { return getImg(item.url); }) .then(addImg); }, Promise.resolve()); }) .then(sleep(300)) .then(function() { $('#done').show(); }); }; $('#run').on('click', run);这里的sleep只是为了看效果加的,可猛击查看demo!当然,Node.js的例子可查看这里。
在这里,Promise.resolve(v)静态方法只是简单返回一个以v为肯定结果的promise,v可不传入,也可以是一个函数或者是一个包含then方法的对象或函数(即thenable)。
类似的静态方法还有Promise.cast(promise),生成一个以promise为肯定结果的promise;
Promise.reject(reason),生成一个以reason为否定结果的promise。
我们实际的使用场景可能很复杂,往往需要多个异步的任务穿插执行,并行或者串行同在。这时候,可以对Promise进行各种扩展,比如实现Promise.all(),接受promises队列并等待他们完成再继续,再比如Promise.any(),promises队列中有任何一个处于完成态时即触发下一步操作。
标准的Promise可参考html5rocks的这篇文章JavaScript Promises,目前高级浏览器如chrome、firefox都已经内置了Promise对象,提供更多的操作接口,比如Promise.all(),支持传入一个promises数组,当所有promises都完成时执行then,还有就是更加友好强大的异常捕获,应对日常的异步编程,应该足够了。
第三方库的Promise现今流行的各大js库,几乎都不同程度的实现了Promise,如dojo,jQuery、Zepto、when.js、Q等,只是暴露出来的大都是Deferred对象,以jQuery(Zepto类似)为例,实现上面的getImg():
function getImg(url) { var def = $.Deferred(); var img = new Image(); img.onload = function() { def.resolve(this); }; img.onerror = function(err) { def.reject(err); }; img.src = url; return def.promise(); };当然,jQuery中,很多的操作都返回的是Deferred或promise,如animate、ajax:
// animate $('.box') .animate({'opacity': 0}, 1000) .promise() .then(function() { console.log('done'); }); // ajax $.ajax(options).then(success, fail); $.ajax(options).done(success).fail(fail); // ajax queue $.when($.ajax(options1), $.ajax(options2)) .then(function() { console.log('all done.'); }, function() { console.error('There something wrong.'); });jQuery还实现了done()和fail()方法,其实都是then方法的shortcut。
处理promises队列,jQuery实现的是$.when()方法,用法和Promise.all()类似。