////////////////////////////////////////////////////////////////////////////////
/**
* 2017/4/7
*
* promise, deferred 的擴充
*
* 以(promise)為核心
*
* 以 series 為主
*
* 若 series 中間有錯,錯誤點的後面就是錯誤
* 與 promise 不同,promise 若錯誤在 catch 後會回到 resolve
*
* 這樣做是方便用在 tree
*/
////////////////////////////////////////////////////////////////////////////////
!(function(root) {
if (typeof root.jQuery === 'function') {
// jquery
$.D_ = $.Deferred_ = Promise_;
} else if (typeof module === 'object' && typeof module.exports === 'object') {
// for node.js
module.exports = Promise_;
} else if (typeof window === 'object') {
root.$D_ = root.$Deferred_ = Promise_;
} else {
return;
}
/* ====================================================================== */
// function async
var asyncFun;
if (typeof setImmediate !== 'undefined') {
asyncFun = function(fn) {
setImmediate(fn);
}
} else if (typeof process !== 'undefined' && process.nextTick) {
asyncFun = function(fn) {
process.nextTick(fn);
}
} else {
asyncFun = function(fn) {
setTimeout(fn, 0);
}
}
////////////////////////////////////////////////////////////////////////////
var PENDING = 1; // 事件仍在處理狀態
var FULFILLED = 2; // 事件已處理完成
var REJECTED = 3; // 事件已處理完,但拋出 reject
////////////////////////////////////////////////////////////////////////////
function Promise_(name) {
if (!(this instanceof Promise_)) {
if (name == null || typeof name === 'string') {
return new Promise_(name);
} else {
// 轉換
return conversion(name);
}
}
/* ------------------------ */
var self = this;
/* ------------------------ */
this.prev; // 上一個 promise
this.next; // 下一個 promise
/* ------------------------ */
this.fn = this.constructor;
// this.Job = this.fn.prototype.Job;
this.name = 'init';
this.id = this.fn.id++;
/* ------------------------ */
this.jobList = [];
/* ------------------------ */
this.fired = false; // 是否已執行過
this.value;
this.reason;
/* ------------------------ */
this.status = PENDING;
if (typeof name === 'function') {
return conversion(name);
} else {
__construct(this);
}
/* ------------------------ */
function __construct(promise) {
if (typeof name === 'string' && name) {
promise.name = name;
}
}
}
/* ---------------------------------------------------------------------- */
// for test
Promise_.id = 0;
/**************************************************************************/
(function(fn) {
// value 可以是任何質,但不能是自己的實例
// 但 value 若是 promise 得承接
// 從此實現與其他庫的 promise 轉接
this.resolve = function(value) {
// debugger;
var self = this;
if (value === this) {
// 不能 resolve(self)
// 會變成無窮回圈
throw TypeError("can't resolve(self)");
}
if (this.fired) {
// 已執行過
return;
}
/* ---------------------------------- */
if (isThenable(value)) {
// 若 resolve(promise)
value.then(function(_value) {
self.resolve(_value);
}, function(_reason) {
self.reject(_reason);
});
return;
} else {
// 一般情況
this.status = FULFILLED;
this.fired = true;
this.value = value;
this._doJob();
}
return this;
};
/* ================================================================== */
this.reject = function(reason) {
// debugger;
if (reason === this) {
// 不能 resolve(self)
// 會變成無窮回圈
throw TypeError("can't reject(self)");
}
/* ---------------------------------- */
if (this.fired) {
// 已執行過
return;
}
this.fired = true;
this.status = REJECTED;
if (arguments.length > 1) {
this.reason = [].slice.call(arguments);
} else {
this.reason = reason;
}
this._doJob();
return this;
};
/* ================================================================== */
// 返回 promise
// 給 return def.promise() 時用的
// 直接返回 promise
this.promise = function() {
return Promise.resolve(this);
}
}).call(Promise_.prototype, Promise_);
/**************************************************************************/
(function(fn) {
// 返回 promise
this.pThen = function(onFulfilled, onRejected) {
var def = this.then(onFulfilled, onRejected);
return Promise.resolve(def);
};
/*
整個程式最重要的地方
*/
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, this.value, this.reason);
} else if (this.status === REJECTED) {
// 立馬執行
errorBack.call(this, onRejected, this.value, this.reason);
}
/* ----------------------------- */
return next_promise;
};
/* ================================================================== */
/**
* call by this
*
* 把 onFulfilled 包起來
*
* 呼叫下一個 promise
*/
function callBack(onFulfilled, value, reason) {
// debugger;
var return_value, return_promise, next_promise = this.next;
if (typeof onFulfilled === 'function') {
return_value = onFulfilled(value);
} else {
// 若沒有設定(onFulfilled)
// 呼叫子 promise
next_promise.resolve(value);
return;
}
/* ---------------------------------- */
// 若有設定(onFulfilled)
if (isThenable(return_value)) {
return_promise = return_value;
// 等待 ret 執行完
return_promise.then(function(_value) {
next_promise.resolve(_value);
}, function(_reason) {
next_promise.reject(_reason);
});
} else {
// 呼叫子 promise
next_promise.resolve(return_value);
}
}
/* ================================================================== */
/**
* call by this
*
* 把 onRejected 包起來
*
* 呼叫下一個 promise
*/
function errorBack(onRejected, value, reason) {
var return_value, return_promise, next_promise = this.next;;
if (typeof onRejected === 'function') {
return_value = onRejected(reason);
// 若要符合 promise 規則啟用下面註解
// promise 只會執行一次 catch(),皆下來的 catch()都不會執行
// return;
} else {
// 若沒有設定(onRejected)
// 正常程序
next_promise.reject(reason);
return;
}
/* ---------------------------------- */
// 若有設定(onRejected)
if (isThenable(return_value)) {
// 正常程序應該是啟用下面
// next_promise.reject(this.reason);
// 但保留這個步驟
// 可以讓發生的錯誤序列轉正(雖不合序列邏輯)
return_promise = return_value;
// 等待 return_promise 執行完
return_promise.then(function(_value) {
next_promise.resolve(_value);
}, function(_reason) {
next_promise.reject(_reason);
});
} else {
// 正常程序
// 不使用 return_value
// 直接傳輸最初的 error 訊息
next_promise.reject(reason);
}
}
}).call(Promise_.prototype, Promise_);
/**************************************************************************/
(function(fn) {
// return promise
this.pCatch = function(onRejected) {
var def = this.catch(onRejected)
return new Promise.resolve(def);
};
this.catch = function(onRejected) {
var promise = this.then(undefined, onRejected);
return promise;
};
/* ================================================================== */
this.pDone = function(onFulfilled) {
var def = this.done(onFulfilled);
return new Promise.resolve(def);
};
this.done = function(onFulfilled) {
var promise = this.then(onFulfilled, undefined);
return promise;
};
/* ================================================================== */
this.pAlways = function(callback) {
var def = this.always(callback);
return new Promise.resolve(def);
};
this.always = function(callback) {
var promise = this.then(function(value) {
return callback(undefined, value);
}, function(reason) {
// 怕 reject(undefined)
return callback(true, reason);
});
return promise;
};
/* ================================================================== */
this.pDelay = function() {
var def = this.delay(time);
return new Promise.resolve(def);
};
this.delay = function(time) {
var promise = this.then(function(value) {
var def = Promise_();
setTimeout(function() {
def.resolve(value);
}, time);
return def;
}, function(reason) {
var def = Promise_();
setTimeout(function() {
def.reject(reason);
}, time);
return def;
});
return promise;
}
}).call(Promise_.prototype, Promise_);
/**************************************************************************/
(function(fn) {
this._doJob = function() {
// debugger;
// 若要合乎 promise 的精神
// 要把下列步驟包在 async 裡面
// 確保 resolve, reject 區塊裡的程式執行完
// 才會接著執行 then 註冊的函式
//
// 但若不用 async 也是沒有太大影響
// 不用 async 可確保 resolve 後馬上準確的執行
// 不用再等排程去執行 then 註冊的 fun
// 若裡面 then 註冊的函式
// 合乎排程
// 而不是 resolve()後馬上調用
asyncFun(function() {
while (this.jobList.length) {
// 表示有等待他完成後要執行的工作
var callback;
var job = this.jobList.shift();
/* ---------------------------------- */
if (this.status === FULFILLED) {
callback = job.getResolve();
} else {
callback = job.getReject();
}
/* ---------------------------------- */
// 與 ypromise 不同
// 每個 then(function)都得進入排程
callback(this.value, this.reason);
}
}.bind(this));
};
}).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.pAll = function(promises) {
var def = fn.all(promises);
return new Promise.resolve(def);
};
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) {
data.detail[i] = undefined;
if (!isThenable(_promise)) {
return;
}
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,
reason: reason,
data: this.detail.slice()
};
this.promise.reject(error_data);
}.bind(this);
};
}).call(Promise_.prototype, Promise_);
////////////////////////////////////////////////////////////////////////////
/**
* 類別方法
*/
(function(fn) {
fn.pRace = function(promises) {
var def = fn.race(promises);
return new Promise.resolve(def);
};
fn.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 (!isThenable(_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_);
////////////////////////////////////////////////////////////////////////////
// tool function
// 判斷是否屬於 promise 之家族
function isThenable(obj) {
return obj && (typeof obj['then'] == 'function');
};
/* ---------------------------------------------------------------------- */
// 轉換
function conversion(obj) {
var res;
if (typeof obj === 'function') {
var _fn = obj;
res = (function() {
var promise = new Promise_();
// 執行任務
_fn(promise);
return promise
})();
}
return res;
};
/* ---------------------------------------------------------------------- */
})(this || {});
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
(function(root) {
if (typeof root.Promise !== 'function') {
return;
}
if (typeof root.jQuery === 'function') {
$.Pd_ = $.Pdeferred_ = PDeferred;
} else {
root.$Pd_ = root.$Pdeferred_ = PDeferred;
}
/* ====================================================================== */
// 用預設 promise 當核心的 Deferred
function PDeferred(fn) {
if (!(this instanceof PDeferred)) {
return new PDeferred(fn);
}
var self = this;
this._resolve;
this.reject;
this.promise = new Promise(function(resolve, reject) {
self._resolve = resolve;
self._reject = reject;
fn(self);
});
}
/* ====================================================================== */
(function() {
this.resolve = function(value) {
this._resolve(value);
};
/* ---------------------------------- */
this.reject = function(reason) {
this._reject(reason);
};
/* ---------------------------------- */
this.promise = function() {
return this.promise;
}
}).call(PDeferred.prototype);
})(this || {});