Promise
Promises模型
Promises是一个异步编程模型,它通过一组API来规范化异步操作。
Promises模型的基本概念可以总结为:
- Promises 作为结果或错误的占位符
- 提供了一种在结果完成或错误发生时的通知方式
对于Promises模型而言,已经提出了多个实现的草案:如Promises/A,Promises/B等等。还有一些可以参看:http://wiki.commonjs.org/wiki/Promises
Promises/A
简称“thenable",事实上,每一个Promise对象都有一个方法:then(fulfilledHandler,errorHandler,progressHandler)。因为Promise对象有三个状态:unfulfilled,fulfilled,failed,也就是未完成的,完成的和失败的。当一个Promise对象由unfulfilled状态变为fulfilled状态后,fulfilledHandler函数会被调用。当进度事件发生时,progressHandler会被调用。通常progress事件可以省略。
jQuery Deferred对象
Deferred概念从jQuery的1.5版本开始引用,它是Promises/A标准的一种衍生实现。通过调用jQuery.Deferred()方法,可以创建Deferred对象,它是一个可以链式操作的对象,并能够注册多个回调函数到回调队列,在异步操作成功或者失败时转达消息。
defer在英文中的意思是延迟,因此我们可以理解为Deferred对象为延迟执行的对象。
Deferred对象有三个动作:resolve(解决)、reject(拒绝)和notify(通知)。
Deferred对象可以通过一些方法主动调用这三个动作:deferred.resolve()、deferred.reject()和deferred.notify()。当然,还有一些回调对应不同的状态,如:
- resolved时调用deferred.done()回调;
- rejected时调用deferred.fail()回调;
- resolved或者rejected时调用deferred.always()回调;
- inProgress时调用deferred.progress()回调;
- resolved、rejected或者inProgress时调用then()回调;
有人可能会问then()和always()有什么区别?其实这两个是相似的,只是then函数有两个参数:resolved时的回调和rejected时的回调,相当于将done()和fail()函数合并了,而always函数只有一个回调函数作为参数,不管是resolved还是rejected时,都会执行这个回调。
1 jQuery.Deferred = function( func ) { 2 /* Callback参数 3 once: 所有回调函数只执行一次 4 memory: 触发回调后对象可以继续添加函数,添加的函数会马上被触发 5 unique: 保证相同的回调函数只能添加一次 6 stopOnFalse: 当回调返回false时停止触发 7 */ 8 var tuples = [ 9 // action, add listener, listener list, final state 10 [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], 11 [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], 12 [ "notify", "progress", jQuery.Callbacks("memory") ] 13 ], 14 state = "pending", 15 promise = { 16 state: function() { 17 return state; 18 }, 19 always: function() { 20 deferred.done( arguments ).fail( arguments ); 21 return this; 22 }, 23 then: function( /* fnDone, fnFail, fnProgress */ ) { 24 var fns = arguments; 25 return jQuery.Deferred(function( newDefer ) { 26 jQuery.each( tuples, function( i, tuple ) { 27 var action = tuple[ 0 ], 28 fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; 29 // deferred[ done | fail | progress ] for forwarding actions to newDefer 30 deferred[ tuple[1] ](function() { 31 var returned = fn && fn.apply( this, arguments ); 32 if ( returned && jQuery.isFunction( returned.promise ) ) { 33 returned.promise() 34 .done( newDefer.resolve ) 35 .fail( newDefer.reject ) 36 .progress( newDefer.notify ); 37 } else { 38 newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); 39 } 40 }); 41 }); 42 fns = null; 43 }).promise(); 44 }, 45 // Get a promise for this deferred 46 // If obj is provided, the promise aspect is added to the object 47 promise: function( obj ) { 48 return obj != null ? jQuery.extend( obj, promise ) : promise; 49 } 50 }, 51 deferred = {}; 52 53 // Keep pipe for back-compat 54 promise.pipe = promise.then; 55 56 // Add list-specific methods 57 jQuery.each( tuples, function( i, tuple ) { 58 var list = tuple[ 2 ], 59 stateString = tuple[ 3 ]; 60 61 // promise[ done | fail | progress ] = list.add 62 promise[ tuple[1] ] = list.add; 63 64 // Handle state 65 if ( stateString ) { 66 list.add(function() { 67 // state = [ resolved | rejected ] 68 state = stateString; 69 70 // [ reject_list | resolve_list ].disable; progress_list.lock 71 }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); 72 } 73 74 // deferred[ resolve | reject | notify ] 75 deferred[ tuple[0] ] = function() { 76 deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); 77 return this; 78 }; 79 deferred[ tuple[0] + "With" ] = list.fireWith; 80 }); 81 82 // Make the deferred a promise 83 promise.promise( deferred ); 84 85 // Call given func if any 86 if ( func ) { 87 func.call( deferred, deferred ); 88 } 89 90 // All done! 91 return deferred; 92 }
此外,还有两个方法:
- deferred.promise():在原来的deferred对象上返回另一个deferred对象,后者只开放与改变执行状态无关的方法(比如done()方法和fail()方法),屏蔽与改变执行状态有关的方法(比如resolve()方法和reject()方法),从而使得执行状态不能被改变。
- deferred.when(): 可以传入多个deferred对象,并为他们指定同一个回调(其实就是deferred队列管理的功能)。
关于Deferred对象的更多细节请看API: http://api.jquery.com/category/deferred-object/
Node.js和Promises
我们知道,node.js是异步编程的,对于新手而言,异步编程导致的一个常见的问题就是异步嵌套过深。解决的方法有若干个,一个是使用Event,也就是在合适的时间emit某一个事件,那么代码就变为了一行一行的on('xxx',callback);另一个解决方法就是使用异步管理的库,如async等;还有就是我们现在讨论的Promises模型。
Nodejs中比较流行的Promises库就是Q Library。它的项目主页是:https://github.com/kriskowal/q
- Q.fcall():创建Promise对象
- Q.defer():生成defer对象,可以通过调用resolve和reject方法等改变对象的状态
- Q.all():等待一个数组中的所有Promise对象都执行完毕
一个例子是:
function get_all_the_things(things) { var the_promises = []; things.forEach(function(thing) { var deferred = Q.defer(); get_a_thing(thing, function(result) { deferred.resolve(result); }); the_promises.push(deferred.promise); }); return Q.all(the_promises); }
还有其他的功能,由于还不熟悉,就先到这里。