2017年4月1日 星期六
(js)Deferred => 遵照 promise 規則版,但不處理 try catch
////////////////////////////////////////////////////////////////////////////////
/**
* 2017/4/1
*
* promise, deferred 的擴充
*
* 以(promise)為核心
*
* 盡量遵守 promise 規則
*
*
*/
////////////////////////////////////////////////////////////////////////////////
!(function(root) {
if (typeof root.jQuery === 'function') {
// jquery
$.PDeferred_ = Promise_;
} else if (typeof module === 'object' && typeof module.exports === 'object') {
// for node.js
module.exports = Promise_;
} else if (typeof window === 'object') {
root.$PDeferred_ = Promise_;
} else {
return;
}
////////////////////////////////////////////////////////////////////////////
var PENDING = 1; // 事件仍在處理狀態
var FULFILLED = 2; // 事件已處理完成
var REJECTED = 3; // 事件已處理完,但拋出 reject
////////////////////////////////////////////////////////////////////////////
function Promise_(name) {
if (!(this instanceof Promise_)) {
if (typeof name === 'string') {
return new Promise_(name);
} else {
// 轉換
return conversion(name);
}
}
/* ------------------------ */
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;
}
Promise_.id = 0;
/* ====================================================================== */
(function(fn) {
this.resolve = function(value) {
this.status = FULFILLED;
/* ---------------------------------- */
if (this.fired) {
// 已執行過
return;
} else {
this.fired = true;
}
/* ---------------------------------- */
this.value = value;
this._doJob();
return this;
};
/* ================================================================== */
this.reject = function(reason) {
this.status = REJECTED;
/* ---------------------------------- */
if (this.fired) {
// 已執行過
return;
} else {
this.fired = true;
}
/* ---------------------------------- */
if (arguments.length > 1) {
this.reason = [].slice.call(arguments);
} else {
this.reason = reason;
}
this._doJob();
return this;
};
}).call(Promise_.prototype, Promise_);
/* ====================================================================== */
(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, 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 args = arguments;
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 args = arguments;
var return_value, return_promise, next_promise = this.next;
if (typeof onRejected === 'function') {
return_value = onRejected(reason);
// 若要符合 promise 規則啟用下面註解
// promise 只會執行一次 catch(),皆下來的 catch()都不會執行
} else {
// promise 的走法
// 還沒處理過 catch,所以繼續傳播 reject
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 {
// promise 的走法
// 已經處理過 catch 改走 resolve
next_promise.resolve(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(function(value) {
return callback(undefined, value);
}, function(reason) {
return callback({
reason: reason
});
});
return promise;
};
/* ================================================================== */
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;
/**
* 執行註冊過的 job
*/
while (this.jobList.length) {
// 表示有等待他完成後要執行的工作
var callback;
var job = this.jobList.shift();
/* ---------------------------------- */
if (this.status === FULFILLED) {
callback = job.getResolve();
} else {
callback = job.getReject();
}
/* ---------------------------------- */
/**
* callback
*/
callback(this.value, this._reason);
}
};
}).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(fn) {
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 (!(_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_);
////////////////////////////////////////////////////////////////////////////
// 判斷是否屬於 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;
} else if (isThenable(obj)) {
// 轉接其他的 promise
res = new Promise_();
obj.then(function(value) {
res.resolve(value);
}, function(reason) {
res.reject(reason);
});
return res;
}
};
})(this || {});
訂閱:
張貼留言 (Atom)
沒有留言:
張貼留言