/**
* promise, deferred 的擴充
*/
(function($) {
if (typeof $.jQuery === 'function') {
/**
* 若有 load jquery
* $.Async(), new $.Async()
*/
$ = $.jQuery;
$.Async = Promise_;
} else if(typeof module === 'object' && module.exports === 'object'){
// for node.js
module.exports = Promise_;
}else {
/**
* 若沒有 load jquery, for window
*
* Deferred(), new Deferred()
*/
$.Deferred = Promise_;
}
////////////////////////////////////////////////////////////////////////////
/**
* 以(promise)為核心
*
*/
////////////////////////////////////////////////////////////////////////////
var PENDING = 1; // 事件仍在處理狀態
var FULFILLED = 2; // 事件已處理完成
var REJECTED = 3; // 事件已處理完,但拋出 reject
////////////////////////////////////////////////////////////////////////////
function Promise_(name) {
if (!(this instanceof Promise_)) {
if (typeof name === 'function') {
var res = (function() {
var promise = new Promise_();
name(promise);
return promise
})();
return res;
} else {
return new Promise_(name);
}
}
/* ------------------------ */
this.prev; // 上一個 promise
this.next; // 下一個 promise
/* ------------------------ */
this.fn = this.constructor;
// this.Job = this.fn.prototype.Job;
this.name = name || 'init';
this.id = this.fn.id++;
/* ------------------------ */
this.jobList = [];
/* ------------------------ */
this.value;
this.reason;
/* ------------------------ */
this.status = PENDING;
/* ================================================================== */
this.resolve = function(value) {
this.status = FULFILLED;
// 刪除 reject(), resolve()
// 避免再次更動狀況
delete this.reject;
delete this.resolve;
this.value = value;
this._doJob();
return this;
};
/* ================================================================== */
this.reject = function(reason) {
this.status = REJECTED;
// 刪除 reject(), resolve()
// 避免再次更動狀況
delete this.reject;
delete this.resolve;
if (arguments.length > 1) {
this.reason = [].slice.call(arguments);
} else {
this.reason = reason;
}
this._doJob();
return this;
};
}
Promise_.id = 0;
/* ====================================================================== */
(function(fn) {
/**
* 整個程式最重要的地方
*
*/
this.then = function(onFulfilled, onRejected) {
// debugger;
var next_promise = this.next = new fn('then');
var self = next_promise.prev = this;
/* ----------------------------- */
if (this.status === PENDING) {
var _callBack = callBack.bind(this, onFulfilled);
var _errorBack = errorBack.bind(this, onRejected);
var job = new this.Job(_callBack, _errorBack);
this.jobList.push(job);
} else if (this.status === FULFILLED) {
// 立馬執行
callBack.call(this, onFulfilled);
} else if (this.status === REJECTED) {
// 立馬執行
errorBack.call(this, onRejected);
}
/* ----------------------------- */
return next_promise;
};
/* ================================================================== */
/**
* call by this
*
* 把 onFulfilled 包起來
*
* 呼叫下一個 promise
*/
function callBack(onFulfilled) {
// debugger;
var return_value, return_promise, next_promise = this.next;
if (typeof onFulfilled === 'function') {
return_value = onFulfilled(this.value);
} else {
// 若沒有設定(onFulfilled)
// 呼叫子 promise
next_promise.resolve(this.value);
return;
}
/* ---------------------------------- */
// 若有設定(onFulfilled)
if (return_value instanceof Promise_) {
return_promise = return_value;
// 等待 ret 執行完
return_promise.then(function(_value) {
// debugger;
next_promise.resolve(_value);
}, function(_reason) {
// debugger;
next_promise.reject(_reason);
});
} else {
// 呼叫子 promise
next_promise.resolve(return_value);
}
}
/* ================================================================== */
/**
* call by this
*
* 把 onRejected 包起來
*
* 呼叫下一個 promise
*/
function errorBack(onRejected) {
// debugger;
var return_value, return_promise, next_promise = this.next;;
if (typeof onRejected === 'function') {
return_value = onRejected(this.reason);
// 若要符合 promise 規則啟用下面註解
// promise 只會執行一次 catch(),皆下來的 catch()都不會執行
// return;
} else {
// 若沒有設定(onRejected)
// 正常程序
next_promise.reject(this.reason);
return;
}
/* ---------------------------------- */
// 若有設定(onRejected)
if (return_value instanceof Promise_) {
return_promise = return_value;
// 等待 return_promise 執行完
return_promise.then(function(_value) {
// debugger;
next_promise.resolve(_value);
}, function(_reason) {
// debugger;
next_promise.reject(_reason);
});
} else {
// 正常程序
next_promise.reject(return_value);
}
}
}).call(Promise_.prototype, Promise_);
////////////////////////////////////////////////////////////////////////////
(function(fn) {
this.catch = function(onRejected) {
var promise = this.then(undefined, onRejected);
return promise;
};
/* ================================================================== */
this.fail = function(onRejected) {
var promise = this.then(undefined, onRejected);
return promise;
};
/* ================================================================== */
this.done = function(onFulfilled) {
var promise = this.then(onFulfilled, undefined);
return promise;
};
/* ================================================================== */
this.always = function(callback) {
var promise = this.then(callback, callback);
return promise;
};
}).call(Promise_.prototype, Promise_);
////////////////////////////////////////////////////////////////////////////
(function(fn) {
/* ================================================================== */
this._doJob = function() {
// debugger;
/**
* 執行註冊過的 job
*/
while (this.jobList.length) {
// 表示有等待他完成後要執行的工作
var callback, outValue;
var job = this.jobList.shift();
/* ---------------------------------- */
if (this.status === FULFILLED) {
outValue = this.value;
callback = job.getResolve();
} else {
outValue = this.reason;
callback = job.getReject();
}
/* ---------------------------------- */
/**
* callback
*/
callback(outValue);
}
};
/* ================================================================== */
}).call(Promise_.prototype, Promise_);
////////////////////////////////////////////////////////////////////////////
(function(fn) {
this.Job = Job;
/**
* class
* 處理 then()
*/
function Job(onFulfilled, onRejected) {
var self = this;
var resolve;
var reject;
__construct();
/**
* 建構式
*/
function __construct() {
(typeof onFulfilled === 'function') && (resolve = onFulfilled);
(typeof onRejected === 'function') && (reject = onRejected);
}
/* ============================================================== */
this.getResolve = function() {
return (typeof resolve === 'function' ? resolve : undefined);
};
/* ============================================================== */
this.getReject = function() {
return (typeof reject === 'function' ? reject : undefined);
};
}
}).call(Promise_.prototype, Promise_);
////////////////////////////////////////////////////////////////////////////
/**
* 類別方法
*/
(function(fn) {
fn.resolve = function(value) {
// debugger;
var promise = new fn();
promise.resolve(value);
return promise;
};
/* ================================================================== */
fn.reject = function(reason) {
// debugger;
var promise = new fn();
promise.reject(reason);
return promise;
};
}).call(Promise_.prototype, Promise_);
////////////////////////////////////////////////////////////////////////////
/**
* 類別方法
*/
(function(fn) {
fn.all = function(promises) {
if (!Array.isArray(promises)) {
throw new TypeError('You must pass an array to all.');
}
var promise = new fn('all');
var jobLength = promises.length;
var data = {
detail: [],
index: undefined,
reason: undefined,
promise: promise,
jobLength: jobLength
};
/* ---------------------------------- */
promises.forEach(function(_promise, i) {
if (!(_promise instanceof Promise_)) {
throw new TypeError('[' + i + '] not instance of Promise_');
}
data.detail[i] = undefined;
var all_ok = get_okFn.call(data, i);
var all_error = get_errorFn.call(data, i);
_promise.then(all_ok, all_error);
});
/* ---------------------------------- */
return promise;
};
/* ================================================================== */
function get_okFn(i) {
return function(value) {
this.detail[i] = value;
if (--this.jobLength < 1) {
// finish
this.promise.resolve(this.detail.slice());
}
}.bind(this);
};
/* ================================================================== */
function get_errorFn(i) {
return function(reason) {
var error_data = {
index: i,
detail: reason,
data: this.detail.slice()
};
this.promise.reject(error_data);
}.bind(this);
};
}).call(Promise_.prototype, Promise_);
////////////////////////////////////////////////////////////////////////////
/**
* 類別方法
*/
(function() {
/* ================================================================== */
this.race = function(promises) {
if (!Array.isArray(promises)) {
throw new TypeError('You must pass an array to all.');
}
var promise = new fn('race');
/* ---------------------------------- */
promises.forEach(function(_promise, i) {
if (!(_promise instanceof Promise_)) {
throw new TypeError('[' + i + '] not instance of Promise_');
}
_promise.then(function(value) {
promise.resolve(value);
}, function(reason) {
promise.reject(reason);
});
});
/* ---------------------------------- */
return promise;
};
}).call(Promise_.prototype, Promise_);
})(this);