2017年2月28日 星期二

(js)Promise 與 deferref 的差異

Promise >>


1.Promise.resolve().reject() 是錯誤的,resolve()後面是不能接reject()
promise狀態一旦改變,就不能再變動

----------------------------------------------------------------------------------------
2.promise.catch(undefine).catch(fun_1).catch(fun_2),只有 fun_1 會被執行





Deferred >>


1.deferred.resolve(1).reject(2)可以執行,但 reject(2)是無效的,也是一旦狀態改變,就不能變動

Ex:

 $.Deferred().resolve(1).reject(2).then(function(data) {
                console.log('ok => ', data);
            }, function(error) {
                console.log('error => ', error);
            });
==> 只會執行 ok

但有個狀況會嚴重所以要用  def.promise()



Ex:

function job_1() {
            var def = $.Deferred();

            setTimeout(() => {
                def.resolve(1);
            }, 200);

            return def;
            // 以下是正確作法
            // return def.promise();
        }

job_1().reject(2).then(function(data) {
                console.log('ok => ', data);
            }, function(error) {
                console.log('error => ', error);
            });

*** 若不用def.promise() 會造成 reject()先執行 ***
----------------------------------------------------------------------------------------
2. deferred 無法使用 serires

Ex:

job1().done(job2).done(job3).done(function(value) {
                console.dir('finish => ', value);
            });

*** 無法如 promise 達成 series效果 ***
----------------------------------------------------------------------------------------
3. $.Deferred.resolve().resolve() 與  $.Deferred.reject().reject() 效果是一樣的,都可執行下去

Ex:

$.Deferred().resolve(1).done(function(value) {
                console.log('done_1 => ', value);

                return 2; // 無用
            }).done(function(value) {
                console.log('done_2 => ', value);
            });

result:
done_1 =>  1
done_2 =>  1

(coding)一系列判斷式後的結果

function job_1() {

var x = 1 && 0 || 5;
var y = 1 && 3 || 5;
var z = 0 && 1 || 3;

console.log(x, y, z);
}

function job_2() {

var x = 1 && 0;
x = x || 5;

var y = 1 && 3;
y = y || 5;

var z = 0 && 1;
z = z || 3;

console.log(x, y, z);
}

job_1();

job_2();

Deferred(以 promise 為核心)

/**
 * 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);

2017年2月25日 星期六

正則

^ $ * + ? { } [ ] \ | ( ) . -
等為正則表達式的保留字元

javascript 的保留字多 /

------------------------------------------------------------------
\d : [0-9]
\w : [A-Za-z0-9_](不含中文)
\s :  空白, 換行
\n :  換行
\b :  詞邊界  
------------------------------------------------------------------
\D   vs   \d
\W  vs   \w
\s   vs   \S
\b   vs   \B

\b => \w頭尾兩端的(\W)
------------------------------------------------------------------
{0, m} :  前一字元匹配 0 到 n 次
{n, }  :  前一字元匹配 n 到 無限 次 
------------------------------------------------------------------
?  :  匹配 0-1 次  
*  :  匹配 0-   次
+  :  匹配 1-   次

------------------------------------------------------------------
[CD][a-z]+   =  (C|D)[a-z]+  =  [CD]{1}[a-z]+

/(http.*\\\\.*|http.*\/\/.*)/  ==>  匹配  /http.*\\\\.*/  or  /http.*\/\/.*/ 

(gray|grey)  =  gr(a|e)y  =  gr[ae]y
------------------------------------------------------------------
重複匹配
/(.)(.) \2\1/  => AD DA
------------------------------------------------------------------
(0 | 1 | 2 | 3 | 4 | 5 ) = [ 0 1 2 3 4 5 ]
------------------------------------------------------------------
向前查找>>
    XXX(?=YYY)  ==> XXX 後面要有 YYY 但只有 XXX會被取代

否定向前查找>>
    XXX(?!YYY)

向後查找>>
    (?<=YYY)XXX  ==> XXX 前面要有 YYY 但只有 XXX會被取代

否定向後查找>>
    (?<!YYY)XXX
------------------------------------------------------------------
關閉貪婪搜尋有用的情況:
=> 有兩個以上的長度條件下,去選擇是否採貪婪搜尋
如 
*
+
{n,m}
------------------------------------------------------------------

預設搜索是貪婪法,如 {n, m} => 優先搜索 m 次有符合就放棄
{n, m}?  則是惰性匹配  => 搜索 n 次有搜索到就放棄
------------------------------------------------------------------
Exp 1>>
(
<hhhh><ggg><hhhh><ggg>
)


(( /<.{4,20}>/  ==> (匹配) array('<hhhh><ggg><hhhh><ggg>') )) 
==>貪婪搜索會用最大化搜尋
1.當搜尋過第一個字'<'後,到了.{4,20}會先取{4,20}中最大的 20 來找
2.若20個任意字後面要跟'>'沒找到,就遞減搜尋直到4個任意字後面有跟'>'

(( /<.{4,20}?>/  ==> (匹配)<hhhh> ))
==>若取消貪婪搜尋
1.當搜尋過第一個字'<'後,到了.{4,20}會先取{4,20}中最小的 4 來找
2.找到'<hhhh>'
3.剩下'<ggg><hhhh><ggg>'
4.重頭再用'/<.{4,20}?>/'匹配剩下的'<ggg><hhhh><ggg>'
------------------------------------------------------------------
Exp 2>>
(
<html><head><title></title></head><body></body></html>
)


/<.*>/ ==> all
/<.*?>/  ==> <html>.........
------------------------------------------------------------------
Exp 3>>
(
255.255.256.255
)

A => /(.{3}\.|.{3}(?=\.))/
B => /(.{3}(?=\.)|.{3}\.)/

A.B都是再檢查4個字與差不多條件....但順序不同結果也不同
規則是由左到右

2017年2月24日 星期五

Ys7

http://www.entertainment14.net/blog/post/88730341-%E3%80%8A%E4%BC%8A%E8%98%877%E3%80%8B%E5%9C%96%E6%96%87%E6%94%BB%E7%95%A5%E5%8F%8A%E5%85%A8%E4%BB%BB%E5%8B%99%E8%AA%AA%E6%98%8E

Ys7

http://www.entertainment14.net/blog/post/92293761-%E4%BC%8A%E8%98%877-%E8%A9%B3%E7%B4%B0%E5%9C%96%E6%96%87%E6%94%BB%E7%95%A5-part4

2017年2月22日 星期三

(js css)getDefaultStyling

var getDefaultStyling = function(tagName){
    if(!tagName) tagName = "dummy-tag-name";

    //  Create dummy iframe

    var iframe = document.createElement("iframe");

    document.body.appendChild(iframe);

    //  Create element within the iframe's document

    var iframeDocument = iframe.contentDocument;
    var targetElement = iframeDocument.createElement(tagName);

    iframeDocument.body.appendChild(targetElement);

    //  Grab styling (CSSStyleDeclaration is live, and all values become "" after element removal)

    var styling = iframe.contentWindow.getComputedStyle(targetElement);
    var clonedStyling = {};

    for(var i = 0, len = styling.length; i < len; i++){
        var property = styling[i];

        clonedStyling[i] = property;
        clonedStyling[property] = styling[property];
    }

    //  Remove iframe

    document.body.removeChild(iframe);

    //  Return cloned styling

    return clonedStyling;
};

var getUniqueUserStyling = function(element){
    var allStyling = window.getComputedStyle(element);
    var defaultStyling = getDefaultStyling(element.tagName);

    var userStyling = {};

    for(var i = 0, len = allStyling.length; i < len; i++){
        var property = allStyling[i];
        var value = allStyling[property];
        var defaultValue = defaultStyling[property];

        if(value != defaultValue){
            userStyling[property] = value;
        }
    }

    return userStyling;
};

2017年2月18日 星期六

(js)isObject

// null, undefined 不會通過
function isObject(value){
                return value === Object(value);
}


var data = [undefined, null, 1, 'hi', [], {}, new Map(), new Man()];

JSON.stringify(data) =>
null [object Null]
1 [object Number]
"hi" [object String]
[] [object Array]
{} [object Object]
{} [object Map]
{"name":"man"} [object Object]

2017年2月17日 星期五

(js)jq.transition

http://ricostacruz.com/jquery.transit/

https://www.npmjs.com/package/jquery.transition

https://github.com/louisremi/jquery.transition.js/

2017年2月15日 星期三

(js)SimpleDeferred(核心採用jquery方法)

if (typeof module === 'object' && typeof module.exports === 'object') {
    module.exports = Deferred;
}
////////////////////////////////////////////////////////////////////////////////
/**
 * 2017/2/28
 * 定型版
 *
 * 內核以(jquery樣式)為主
 *
 */
////////////////////////////////////////////////////////////////////////////////


function Deferred() {
    if (!(this instanceof Deferred)) {
        return new Deferred();
    }
    var SimpleDeferred = Deferred.prototype.core;
    return new SimpleDeferred();
}
////////////////////////////////////////////////////////////////////////////////

/**
 * Deferred 登錄 class_SimpleDeferred
 */
(function(_self) {
    _self.prototype.core = SimpleDeferred;

    var PENDING = 1; // 事件仍在處理狀態
    var FULFILLED = 2; // 事件已處理完成
    var REJECTED = 3; // 事件已處理完,但拋出 reject


    ////////////////////////////////////////////////////////////////////////////

    function SimpleDeferred() {

        this.parent = _self;
        this.Job = Job;
        this.Promise = Promise_;
        this.fn = this.constructor;
        /* ------------------------ */

        this.id = this.fn.id++; // for test
        this.value;
        this.reason;
        this.status = PENDING; // 需要做成 primative
        this.jobList = [];

    }

    SimpleDeferred.id = 0;
    ////////////////////////////////////////////////////////////////////////////
    (function() {

        /**
         * 註冊 job
         */
        this.prototype.then = function(onFulfilled, onRejected) {
            var res, self = this;

            /* ---------------------------------- */
            // check
            if (onFulfilled != null && typeof onFulfilled !== 'function') {
                throw new TypeError('then.args[0] must be function');
            }

            if (onRejected != null && typeof onRejected !== 'function') {
                throw new TypeError('then.args[1] must be function');
            }

            /* ---------------------------------- */

            if (this.status === PENDING) {
                // 若任務尚在執行

                var job = new this.Job(onFulfilled, onRejected);
                this.jobList.push(job);

            } else {

                if (this.status === FULFILLED) {
                    // 任務已經正常結束
                    (typeof onFulfilled === 'function') && (res = onFulfilled(this.value));

                } else {
                    // 任務已經異常結束
                    (typeof onRejected === 'function') && (res = onRejected(this.reason));
                }

                (res instanceof SimpleDeferred) && (self = res);

            }
            /* ---------------------------------- */
            return self;
        };


        /* ================================================================== */
        /**
         * 通知執行狀態
         *
         * @param {boolean} specialCondition: 是否是特殊情況
         * 通常已經執行過的的,不能再 resolve()
         * 但某些狀況會例外
         *
         */
        this.prototype.resolve = function(value) {

            this.value = value;
            /* ------------------------ */
            this.status = FULFILLED;
            /* ------------------------ */
            /**
             * 執行註冊過的 job
             */
            this._doJob();

            return this;
        };

        /* ================================================================== */
        /**
         * 通知執行狀態
         * @param {boolean} specialCondition: 是否是特殊情況
         * 通常已經執行過的的,不能再 reject()
         * 但某些狀況會例外
         */
        this.prototype.reject = function(reason) {
            /* ------------------------ */

            if (arguments.length > 1) {
                this.reason = [].slice.call(arguments);
            } else {
                this.reason = reason;
            }
            /* ------------------------ */
            this.status = REJECTED;
            /* ------------------------ */
            /**
             * 執行註冊過的 job
             */
            this._doJob();

            return this;
        };
        /* ================================================================== */
        this.prototype.done = function(onFulfilled) {
            var self = this.then(onFulfilled, undefined);
            return self;
        };
        /* ================================================================== */
        this.prototype.catch = function(onRejected) {
            var self = this.then(undefined, onRejected);
            return self;
        };
        /* ================================================================== */
        this.prototype.fail = function(onRejected) {
            var self = this.then(undefined, onRejected);
            return self;
        };

        this.prototype.promise = function() {
            var promise = new this.Promise(this);
            return promise;
        };

    }).call(SimpleDeferred);
    ////////////////////////////////////////////////////////////////////////////
    (function() {
        /**
         * 執行所有註冊的任務
         */
        this.prototype._doJob = function() {
            /**
             * 執行註冊過的 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();
                }
                /* ---------------------------------- */
                if (typeof callback !== 'function') {
                    continue;
                }
                /* ---------------------------------- */
                // 比較麻煩之處
                var res = callback(outValue);
                /* ---------------------------------- */
                if ((res instanceof SimpleDeferred) || (res instanceof this.Promise)) {
                    if (res instanceof this.Promise) {
                        res = res.def;
                    }

                    // 把責任交出去
                    res.jobList = res.jobList.concat(this.jobList);

                    this.jobList = [];

                    // 檢查 res 的狀態
                    switch (res.status) {
                        case PENDING:
                            /* do nothing,等待他完成事項 */
                            break;
                        case FULFILLED:
                            /**
                             * 因為 res 已經執行過 resolve
                             * 所以要把 resolve(value)再傳一遍
                             */
                            res.resolve(res.value);
                            break;
                        case REJECTED:
                            /**
                             * 因為 res 已經執行過 reject
                             * 所以要把 reject(reason)再傳一遍
                             */
                            res.reject(res.reason);
                            break;
                        default:
                            break;
                    }
                    break;
                } else {
                    continue;
                }
                /* ---------------------------------- */
            }
        };
    }).call(SimpleDeferred);
    ////////////////////////////////////////////////////////////////////////////
    /**
     * 處理 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);
        };
    }
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Deferred 的包裹
     */

    function Promise_(deferred) {
        this.def = deferred;
    }

    (function() {
        this.then = function(onFulfilled, onRejected) {
            this.def.then(onFulfilled, onRejected);
            return this;
        };

        this.done = function(onFulfilled) {
            this.def.done(onFulfilled);
            return this;
        };

        this.fail = function(onRejected) {
            this.def.fail(onRejected);
            return this;
        };

        this.catch = function(onRejected) {
            this.def.catch(onRejected);
            return this;
        };
    }).call(Promise_.prototype);

})(Deferred);
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////

(function(_self) {
    /**
     * Deferred 類別方法
     *
     * 只有全部任務完成才能 resolve
     * 其中之一拋出 reject 就全 out
     */
    _self.all = function(defList) {

        if (!Array.isArray(defList)) {
            throw new TypeError('arg must be array');
        }
        var all_def = Deferred();

        data = {
            self: all_def,
            values: [],
            reasons: undefined,
            count: defList.length
        };
        /* ---------------------------------- */
        // 為每個 deferred 註冊事件
        for (var i = 0; i < defList.length; i++) {
            data.values[i] = undefined;

            var _def = defList[i];
            bindEvent_1(_def, data, i);
        }
        return all_def;
    };


    function bindEvent_1(_def, data, index) {
        var self = data.self;

        _def.then(function(value) {
            data.values[index] = value;

            if (--data.count == 0) {
                // defList 都完成
                self.resolve(data.values);
            }
        }, function(reason) {

            var values = data.values.slice(0);
            self.reject(reason, values);
        });
    };
    /* ====================================================================== */

    /**
     * Deferred 類別方法
     */
    _self.race = function(defList) {
        if (!Array.isArray(defList)) {
            throw new TypeError('arg must be array');
        }

        var race_def = Deferred();

        data = {
            self: race_def,
            values: undefined,
            reasons: undefined,
            count: defList.length
        };
        /* ---------------------------------- */
        // 為每個 deferred 註冊事件
        for (var i = 0; i < defList.length; i++) {
            data.values[i] = undefined;

            var _def = defList[i];
            bindEvent_2(_def, data, i);
        }

        return race_def;
    };


    function bindEvent_2(_def, data, index) {
        var self = data.self;

        _def.then(function(value) {
            // defList 都完成
            self.resolve(values);
        }, function(reason) {
            self.reject(reason);
        });
    }
})(Deferred);
////////////////////////////////////////////////////////////////////////////////

2017年2月14日 星期二

(js)Deferred

/* jq1.6.2 */


var __id = 0; // for test
var // Promise methods
    promiseMethods = "done fail isResolved isRejected promise then always pipe".split(" "),
    // Static reference to slice
    sliceDeferred = [].slice;

jQuery.extend({
    // Create a simple deferred (one callbacks list)
    _Deferred: function() {
        debugger;

        var callbacks = []; // callbacks list
        var fired; // stored [ context , args ]

        var firing; // to avoid firing when already doing so

        var cancelled; // flag to know if the deferred has been cancelled

        // the deferred itself
        var deferred = {

            // done( f1, f2, ...)
            done: function() {
                debugger;

                if (!cancelled) {
                    var args = arguments,
                        i,
                        length,
                        elem,
                        type,
                        _fired;
                    if (fired) {
                        _fired = fired;
                        fired = 0;
                    }
                    for (i = 0, length = args.length; i < length; i++) {
                        elem = args[i];
                        type = jQuery.type(elem);
                        if (type === "array") {
                            deferred.done.apply(deferred, elem);
                        } else if (type === "function") {
                            callbacks.push(elem);
                        }
                    }
                    if (_fired) {
                        deferred.resolveWith(_fired[0], _fired[1]);
                    }
                }
                return this;
            },

            // resolve with given context and args
            resolveWith: function(context, args) {
                debugger;

                if (!cancelled && !fired && !firing) {
                    // make sure args are available (#8421)
                    args = args || [];
                    firing = 1;
                    try {
                        while (callbacks[0]) {
                            callbacks.shift().apply(context, args);
                        }
                    } finally {
                        fired = [context, args];
                        firing = 0;
                    }
                }
                return this;
            },

            // resolve with this as context and given arguments
            resolve: function() {
                debugger;

                deferred.resolveWith(this, arguments);
                return this;
            },

            // Has this deferred been resolved?
            isResolved: function() {
                return !!(firing || fired);
            },

            // Cancel
            cancel: function() {
                debugger;

                cancelled = 1;
                callbacks = [];
                return this;
            }
        };
        deferred.id = __id++; // for test

        return deferred;
    },

    // Full fledged deferred (two callbacks list)
    Deferred: function(func) {
        debugger;

        var deferred = jQuery._Deferred();
        var failDeferred = jQuery._Deferred();
        var promise;



        // Add errorDeferred methods, then and promise
        jQuery.extend(deferred, {
            then: function(doneCallbacks, failCallbacks) {
                debugger;

                deferred.done(doneCallbacks).fail(failCallbacks);

                return this;
            },
            always: function() {
                return deferred.done.apply(deferred, arguments).fail.apply(this, arguments);
            },
            fail: failDeferred.done, // here is important(把 deferred 導向 failDeferred 的閉包)
            rejectWith: failDeferred.resolveWith,
            reject: failDeferred.resolve,
            isRejected: failDeferred.isResolved,
            pipe: function(fnDone, fnFail) {
                return jQuery.Deferred(function(newDefer) {
                    jQuery.each({
                        done: [fnDone, "resolve"],
                        fail: [fnFail, "reject"]
                    }, function(handler, data) {
                        var fn = data[0],
                            action = data[1],
                            returned;
                        if (jQuery.isFunction(fn)) {
                            deferred[handler](function() {
                                returned = fn.apply(this, arguments);
                                if (returned && jQuery.isFunction(returned.promise)) {
                                    returned.promise().then(newDefer.resolve, newDefer.reject);
                                } else {
                                    newDefer[action](returned);
                                }
                            });
                        } else {
                            deferred[handler](newDefer[action]);
                        }
                    });
                }).promise();
            },
            // Get a promise for this deferred
            // If obj is provided, the promise aspect is added to the object
            promise: function(obj) {
                debugger;

                if (obj == null) {
                    if (promise) {
                        return promise;
                    }
                    promise = obj = {};
                }
                var i = promiseMethods.length;
                while (i--) {
                    obj[promiseMethods[i]] = deferred[promiseMethods[i]];
                }
                return obj;
            }
        });

        debugger;

        // Make sure only one callback list will be used
        deferred.done(failDeferred.cancel); // 執行環境的閉包屬 deferred
        deferred.fail(deferred.cancel); // 執行環境的閉包屬 failDeferred

        // Unexpose cancel
        delete deferred.cancel;

        // Call given func if any
        if (func) {
            func.call(deferred, deferred);
        }
        return deferred;
    },

    // Deferred helper
    when: function(firstParam) {
        debugger;

        var args = arguments,
            i = 0,
            length = args.length,
            count = length,
            deferred = length <= 1 && firstParam && jQuery.isFunction(firstParam.promise) ?
            firstParam :
            jQuery.Deferred();

        function resolveFunc(i) {
            debugger;

            return function(value) {
                debugger;

                args[i] = arguments.length > 1 ? sliceDeferred.call(arguments, 0) : value;
                if (!(--count)) {
                    // Strange bug in FF4:
                    // Values changed onto the arguments object sometimes end up as undefined values
                    // outside the $.when method. Cloning the object into a fresh array solves the issue
                    deferred.resolveWith(deferred, sliceDeferred.call(args, 0));
                }
            };
        }
        if (length > 1) {
            for (; i < length; i++) {
                if (args[i] && jQuery.isFunction(args[i].promise)) {
                    args[i].promise().then(resolveFunc(i), deferred.reject);
                } else {
                    --count;
                }
            }
            if (!count) {
                deferred.resolveWith(deferred, args);
            }
        } else if (deferred !== firstParam) {
            deferred.resolveWith(deferred, length ? [firstParam] : []);
        }
        return deferred.promise();
    }
});

2017年2月13日 星期一

(js)promise小問題修正版

////////////////////////////////////////////////////////////////////////////////
/**
 * 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 || {});