/**
* 2017/2/14
*
* fix from https://github.com/ygm125/promise
*/
////////////////////////////////////////////////////////////////////////////////
(function(exports) {
if (typeof window === 'object') {
// is window
exports._Promise = Promise;
try {
new Promise();
return;
} catch (e) {
exports.Promise = Promise;
}
} else if (typeof module === 'object' && module.exports) {
// is node.js
module.exports = Promise;
} else {
return;
}
////////////////////////////////////////////////////////////////////////////
var PENDING = undefined; // 事件仍在處理狀態
var FULFILLED = 1; // 事件已處理完成
var REJECTED = 2; // 事件已處理完,但拋出 reject
/**
* @param {string} name 測試用,命名以方便與 then() 生成的 promise做區分
*/
function Promise(_resolver, name, parent) {
// debugger;
if (typeof _resolver !== 'function') {
throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
}
if (!(this instanceof Promise)) {
// 當作函式用的話
return new Promise(_resolver, name);
}
/* ---------------------------------- */
// for test 方便辨別 promise 身份
this._parent = (parent instanceof Promise ? parent : undefined); // 紀錄上一個 promise
this.guid = Promise.prototype.guid++; // for test
this._name = (name ? (name + '(' + this.guid + ')') : this.guid); // for test
/* ---------------------------------- */
var promise = this;
promise._value;
promise._reason;
promise._status = PENDING;
/* ---------------------------------- */
promise._resolves = []; // then([0])的工作都放這
promise._rejects = []; // then([1])的工作都放這
/* ---------------------------------- */
try {
// 處理 promise 程序拋出 error
_resolver(resolve, reject);
} catch (error) {
reject(error);
}
/* ================================== */
function resolve(value) {
promise._status = FULFILLED;
reportFinshJob.call(promise, value);
}
/* ---------------------------------- */
function reject(reason) {
promise._status = REJECTED;
reportFinshJob.call(promise, reason);
}
};
/* ====================================================================== */
Promise.prototype.guid = 0; // for test
////////////////////////////////////////////////////////////////////////////
/**
* very important
*/
Promise.prototype.then = function(onFulfilled, onRejected) {
// debugger;
var promise = this;
// 每次返回一个promise,保证是可thenable的
return new Promise(function(resolve, reject) {
// debugger;
if (promise._status === PENDING) {
promise._resolves.push(callback);
promise._rejects.push(errback);
} else if (promise._status === FULFILLED) { // 状态改变后的then操作,立刻执行
callback(promise._value);
} else if (promise._status === REJECTED) {
errback(promise._reason);
}
/* ============================================ */
function callback(value) {
var ret;
try {
/**
* 修改此處
*
* 處理 then.onFulfilled() 萬一出錯
*/
ret = (typeof onFulfilled === 'function' && onFulfilled(value));
} catch (error) {
reject(error);
}
ret = ret || value;
if (isThenable(ret)) {
ret.then(function(value) {
resolve(value);
}, function(reason) {
reject(reason);
});
} else {
resolve(ret);
}
}
/* ---------------------------------------- */
function errback(reason) {
var _reason;
try {
_reason = (typeof onRejected === 'function' && onRejected(reason));
} catch (error) {
_reason = error;
} finally {
_reason = _reason || reason;
reject(_reason);
}
}
}, 'then', promise);
};
/* ====================================================================== */
Promise.prototype.catch = function(onRejected) {
return this.then(undefined, onRejected)
};
/* ====================================================================== */
Promise.prototype.delay = function(ms, val) {
return this.then(function(ori) {
return Promise.delay(ms, val || ori);
})
};
/* ====================================================================== */
Promise.delay = function(ms, val) {
return Promise(function(resolve, reject) {
setTimeout(function() {
resolve(val);
}, ms);
})
};
/* ====================================================================== */
Promise.resolve = function(arg) {
return Promise(function(resolve, reject) {
resolve(arg)
}, 'resolve');
};
/* ====================================================================== */
Promise.reject = function(arg) {
return Promise(function(resolve, reject) {
reject(arg)
}, 'reject');
};
/* ====================================================================== */
Promise.all = function(promises) {
if (!isArray(promises)) {
throw new TypeError('You must pass an array to all.');
}
return Promise(function(resolve, reject) {
var i = 0,
result = [],
len = promises.length,
count = len
function resolver(index) {
return function(value) {
resolveAll(index, value);
};
}
function rejecter(reason) {
reject(reason);
}
function resolveAll(index, value) {
result[index] = value;
if (--count == 0) {
resolve(result)
}
}
for (; i < len; i++) {
promises[i].then(resolver(i), rejecter);
}
}, 'all');
};
/* ====================================================================== */
Promise.race = function(promises) {
if (!isArray(promises)) {
throw new TypeError('You must pass an array to race.');
}
return Promise(function(resolve, reject) {
var i = 0,
len = promises.length;
function resolver(value) {
resolve(value);
}
function rejecter(reason) {
reject(reason);
}
for (; i < len; i++) {
promises[i].then(resolver, rejecter);
}
}, 'race');
};
////////////////////////////////////////////////////////////////////////////
function isArray(obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
};
/* ====================================================================== */
/**
* 若對象含有.then()
*/
function isThenable(obj) {
return (obj && typeof obj['then'] == 'function');
};
/* ====================================================================== */
/**
* promise.resolve() || promise.reject() 後要做的事
*/
function reportFinshJob(value) {
// debugger;
var promise = this;
publish.call(promise, value);
};
/* ====================================================================== */
/**
* 執行(._resolves)or(_rejects)
*/
function publish(val) {
var promise = this,
fn,
st = promise._status === FULFILLED;
// 任務隊列
var queue = promise[st ? '_resolves' : '_rejects'];
while (fn = queue.shift()) {
// 裡面是 callback || errback 包住下一個 promise
val = fn.call(promise, val) || val;
}
promise[st ? '_value' : '_reason'] = val;
promise._resolves = [];
promise._rejects = [];
}
})(this || {});