经过对比之后,决心学习jQuery,自己打造一个Deferred对象.var util = require('./util.js');function Callbacks() {
var list = [], fireIndex = -1, firing, momery, self = { add: function () { if (list) { if (firing) { fireIndex = list.length - 1; } (function add(args) { for (var i = 0; i < args.length; i++) { var value = args[i]; var type = util.type(value); if (type === 'function') { list.push(value); } else if (type === 'array') { add(value); } } })(arguments); if (!firing && momery) { this.fire.apply(this, momery); } } return this; }, remove: function () { for (var i = 0; i < arguments.length; i++) { var value = arguments[i]; var index = list.indexOf(v) if (index > -1) { list.splice(index, 1); } if (index <= firingIndex) { firingIndex--; } } return this; }, has: function (fn) { return fn ?list.indexOf(fn) > -1 :list.length > 0; }, empty: function () { if (list) { list = []; } return this; }, fire: function () { momery = util.slice.call(arguments); if (!firing) { fireing = true; while (++fireIndex < list.length) { list[fireIndex].apply(this, momery); } list = []; fireIndex = -1; } firing = false; } } return self; } function Deferred() { var state = "pending", scalls = Callbacks(), fcalls = Callbacks(), promise = { state: function () { return state; }, always: function () { var args=util.slice.call(arguments);
this.done(args).fail(args);
return this; }, then: function (success, fail) { switch (state) { case "resolved": scalls.add(success); break; case "rejected": fcalls.add(fail); break; default: scalls.add(success); fcalls.add(fail); break; } return this; }, done: function (success) {
this.then(success, null);
return this; }, fail: function (fail) { this.then(null, fail); return this; }, resolve: function () { if (state === "pending") { state = "resolved"; scalls.fire.apply(this, arguments); } return this; }, reject: function () { if (state === "pending") { state = "rejected"; fcalls.fire.apply(this, arguments); } return this; } }; return promise; } module.exports = { Deferred: function () { return Deferred(); }, when: function () { var deferred = Deferred(), args = util.slice.call(arguments), ramin = len = arguments.length, context; function update(i) { return function (value) { context[i] = arguments.length > 1?util.slice.call(arguments):value; if (!--ramin) { deferred.resolve.apply(deferred, context); } } } if (len > 0) { context = new Array(len); for (var i = 0; i < len; i++) { var def = args[i]; if (def && util.type(def.then) === 'function') { def.then.call(def, update(i), deferred.reject); } else { update(i)(def); } } } return deferred; } }
测试用例:
var Q = require('./Deferred.js'); var deferred = Q.Deferred(); setTimeout(function () { deferred.reject(1).then(function (r) { console.log("r4:" + r); }, function (e) { console.log("e4:" + e); }); }, 5000); deferred.then(function (r) { console.log("r:" + r); }, function (e) { console.log("e:" + e); }); setTimeout(function () { deferred.reject(2).then(function (r) { console.log("r2:" + r); }, function (e) { console.log("e2:" + e); }); }, 10000); deferred.then(function (r) { console.log("r3:" + r); }, function (e) { console.log("e3:" + e); }); function A(){ var d = Q.Deferred().done(function (r) { console.log('A'+r); }); setTimeout(function () { d.resolve('阿'); }, 1000); return d; } function B() { var d = Q.Deferred().done(function (r) { console.log('B' +r); /*d.resolve('b');*/ }); return d; } function C() { var d = Q.Deferred().done(function (r) { console.log('C' +r); /*d.resolve('c');*/ }); setTimeout(function () { }, 1000); return d; } Q.when(1, A() , 3).done(function (a,b,c) { console.log(a); console.log(b); console.log(c); }).fail(function (err) { console.log('err' + err); });
实际应用包装:
var d=require('./lib/deferred'); var util=require('./lib/util'); var mysql = require('mysql'); var settings = require('./config'); exports.process = function (wrappers) { var con = mysql.createConnection(settings.db); console.time('process'); con.beginTransaction(function (err) { if (err) { console.error(err); } else{ (function (wrappers){ var callee=arguments.callee; if(util.type(wrappers)=='array'){ function wrapped(wraper){ var deferred= d.Deferred(); con.query(wraper.sql, wraper.data,function(err,rows){ if(err){ deferred.reject(err); } else{ deferred.resolve(rows); } }); deferred.then(wraper.scall,wraper.fcall); return deferred; } var deferreds=[]; wrappers.forEach(function(wraper,i){ deferreds.push(wrapped(wraper)); }); d.when.apply(null,deferreds).then(function(){ con.commit(function (err) { if (err) { console.error(err); return con.rollback(function () { con.end(function (err) { if(err){console.error(err);} }); }); } console.info('process success!'); }); },function(err){ console.error(err); con.rollback(function () { con.end(function (err) { if (err) { console.error(err); } }); }); }).always(function () { console.timeEnd('process'); }); }else if(util.type(wrappers)=='object'){ callee([wrappers]); } })(wrappers); } }); };