promise api 与应用场景
promise 是 commonjs 社区中提出的异步规范,其简洁直观的 api 使得异步读值操作更易于理解和使用,主要 api 包括:
API
Defer
功能实现者调用 Defer() 后产生 Defer 对象,它包括 promise 属性以及 resolve 和 reject 方法
promise 对象
功能调用者通过调用 promise 的 then 方法添加成功回调和失败回调函数。多次调用 then 添加的回调函数最终“并行”执行,互相独立。
resolve/reject
功能实现者通过 resolve/reject 来通知 promise 对象成功与否并传递指定的参数给调用者的回调函数来执行。
note:
通过分离 Defer 对象和 promise 对象,可以达到功能实现者和调用者的权限分离,符合 Object-capability-model 从而更有利于系统的安全。
all
通过 all 方法生成一个新的 promise 对象,通过该对象可以监控一批 promise 对象,只有当全部 promise 都成功时才使得新的 promise 对象成功,否则只要有一个 promise 对象失败那么新生成的 promise 就算做失败。
when
调用 when 可以统一获取 promise 对象和非 promise 对应的值,屏蔽异步的差异。
api 使用
then
1. 统一的回调注册
then 方法提供了一个统一的回调注册接口,无论注册时该异步 promise 是否已经成功,都会保证回调函数会被及时调用。
例如:
可以成功前注册:
var defer= S.Defer(); defer.promise.then(function(v){ alert(v===1); }); setTimeout(function(){ defer.resolve(1); },5000);
也可以在成功后注册(仍然会被调用)
var defer= S.Defer(); defer.resolve(1); setTimeout(function(){ defer.promise.then(function(v){ alert(v===1); }); },5000);
2. 链式操作
then 会返回一个新的 promise 对象,那么在这个返回对象上可以继续调用 then 方法,而参数值则是上一个 then 方法的返回值:
var defer = S.Defer(); defer.promise.then(function(v){ alert(v===1); return v+1; }).then(function(v2){ alert(v2===2); }); defer.resolve(1);
3. 嵌套链式操作
更进一步:回调函数也可以返回一个 promise 对象,那么 then 返回的 promise 对象则会等待回调函数返回的 promise 对象成功后才算成功,并且最后一个 then 的回调函数的参数为 导致前一个 then 回调函数返回的 promise 对象成功 的值:
var defer = S.Defer(); defer.promise.then(function(v){ alert(v===1); var d2= S.Defer(); setTimeout(function(){ d2.resolve(2); },1000); return d2.promise; }).then(function(v2){ alert(v2===2); }); defer.resolve(1);
all
使用 all 也可以达到嵌套调用的效果(同时等待多个 promise 成功),并可一次性得到导致所有 promise 成功的值:
var defer = S.Defer(); var defer2 = S.Defer(); setTimeout(function(){ defer2.resolve(2); defer.resolve(1); },1000); S.Promise.all([defer.promise,defer2.promise]).then(function(vs){ alert(vs[0]===1); alert(vs[1]===2); });
when
使用 when 可以不加区别得对待 promise 对象和非 promise 对象,通过成功回调可以一致得获取最终结果:
function check(p){ S.Promise.when(p,function(v){ alert(v===1); }); } var defer=S.Defer(); defer.resolve(1); check(1); // => alert(true) check(defer.promise); //=> alert(true);
使用场景
domReady
domReady 是个异步过程,具备以下特征使得它非常适合使用 promise 来实现:
1. domReady 添加回调时可能该事件已经触发过。
2. 多个 domReady 添加的回调并不需要前后依赖,并且要求其中一个回调失败并不影响其他回调(即回调间独立)
那么内部实现即可将 S.ready(fn) 的调用转交给内部的 readyDefer.promise.then。那么当浏览器 domready 时只需简单调用 readyDefer.resolve(S) 即可.
io/ajax
io/ajax 一般也是异步 io,通过 promise 实现 io,那么就可以摆脱必须将处理逻辑统统写在一处的限制:
var r=S.io({...}); r.then(function(v){ // logic 1 }); r.then(function(v){ // logic 2 });
并且通过嵌套以及 all 也可以很容易支持多个系统间的相互时序依赖。
串行:
var r=S.io({..}); r.then(function(v){ return S.io({..,data:{v2:v+1}}) }).then(function(v2){ // logic v2 })
并行:
S.Promise.all([S.io({url:'u1'}),S.io({url:'u2'})]).then(function(vs){ alert("u1+u2 => " + (vs[0]+vs[1])); });
Module Loader
同 io 类似通过用嵌套以及 all 来管理多个 module 代码文件的串行以及并行加载,可以更加清晰得组织代码结构。
Refer:
KISSY.Defer API & KISSY.Promise API
http://wiki.commonjs.org/wiki/Promises
http://en.wikipedia.org/wiki/Futures_and_promises#Read-only_views
http://en.wikipedia.org/wiki/Object-capability_model
https://github.com/kriskowal/q
http://www.sitepen.com/blog/2010/05/03/robust-promises-with-dojo-deferred-1-5/
http://dojotoolkit.org/documentation/tutorials/1.6/deferreds/