1

特定のCookieを処理するRevealing Moduleパターンでカスタムライブラリを作成し、Cookieの「Getter」関数の戻り値としてjQuery Promiseを使用して、関数を呼び出す人が最初に設定される前に関数を更新しないようにしようとしています、したがって同期を維持します。

下記参照:

/**
 * Handles the state cookie for a browser.
 *
 * JS DEPENDENCIES:
 * - jQuery Cookie plugin
 *
 * DOM DEPENDENCIES:
 * - None
 *
 */
var myUtilities = myUtilities || {};

myUtilities.stateManager = (function() {
    var cookieName = 'us_state';

    /**
     * Find geolocation state / set cookie
     * The passed deferred object only gets set as resolved if the AJAX response has the resulting data we need. Otherwise it is rejected.
     *
     * @param  {Object} position Passed from 'navigator.geolocation.getCurrentPosition'. Contains browser's approximation of its current latitude+longitude.
     * @return {Object}          The promise resolution (resolve or reject). Resolved has a String of state abbreviation in lowecase. Rejected is empty.
     */
    function _getLocation(position) {
        var latitude  = position.coords.latitude,
            longitude = position.coords.longitude;

        /* TEST VALUES */
        /* CA coords */
        // latitude  = '37.7833';
        // longitude = '-122.4167';
        /* AZ coords */
        // latitude  = '33.45';
        // longitude = '-112.0667';

        // If this errors out due to CORS issue (or similar issue) of if the return value doesn't match then we set the promise to reject
        return $.ajax({
            url: 'https://maps.googleapis.com/maps/api/geocode/json?latlng=' + latitude + ',' + longitude,
            dataType: "json"
        });
    }

    /**
     * Defer for getCurrentPosition callback
     * Create an anonymous function to handle success; accepts a Position object as argument, and calls _getLocation() passing in the position object.
     * When AJAX promise is complete evalute the data to find the state abbreviation.
     * Reject a failed call for getCurrentPosition (user did not allow/timeout on browser's request to use geolocation)
     *
     * @var {Object} $df jQuery Deferred object
     * @return {Object} jQuery Promise
     */
    function _deferGetLocation() {
        var $df = $.Deferred();

        if ("geolocation" in navigator) {
            navigator.geolocation.getCurrentPosition(
                function(position) {
                    _getLocation(position)
                        .then(function(data) {
                            if (data.length !== 0) {
                                var result  = data.results[0],
                                    address = '',
                                    state   = '';

                                // A for-loop is used because the response changes based on the address that Google API returns (a single search into a specific part of the data Object is not always successful evne though the data may be in there)
                                for (var i = 0, len = result.address_components.length; i < len; i++) {
                                    address = result.address_components[i];

                                    if (address.types.indexOf('administrative_area_level_1') >= 0) {
                                        // By returning here we exit the loop as soon as we get a match, like a 'break'
                                        $df.resolve(address.short_name.toLowerCase());
                                        break;
                                    }
                                }
                            }
                        });
                    });
        } else {
            $df.reject();
        }

        return $df.promise();
    }

    /**
     * Either get the get cookie or set it now.
     * If the cookie exists we resolve the promise immediately, else wait for the geolocation to be resolved, set state cookie and resolve.
     *
     * @var {Object} $df         jQuery Deferred object
     * @var {String} stateString state, 2 character abbreviation format
     * @return {Object} Promise with a String for the callback (two-character value indicating which state the user is in)
     */
    function _getStateCookie(){
        var $df = $.Deferred();

        if ($.cookie(cookieName)) {
            $df.resolve($.cookie(cookieName));
        } else {
            _deferGetLocation()
                .then(function(state) {
                    $df.resolve(_setStateCookie(state));
                });
        }

        return $df.promise();
    }

    /**
     * Set the 'cookieName' cookie to a desired state, or default to 'co'
     *
     * @param {String} state The value of the cookie as a 2 character length state abbreviation
     * @param {Datetime} expirationDate Days until the cookie expires
     */
    function _setStateCookie (state, expirationDate){
        state          = ( typeof state == 'undefined' || !_isValidState(state) ) ? 'co' : state;
        expirationDate = ( typeof expirationDate == 'undefined' ) ? 365 : expirationDate;

        $.cookie(cookieName, state, { path: '/', expires: expirationDate });

        // Offer an event listener for this cookie
        $(document).trigger('state-utility.cookieChange');

        return state;
    }

    /**
     * Validates a given string against our predetermined "valid states" (AZ, CA, CA).
     * Returns  true if valid, false otherwise.
     * Case-sensitive, AZ == az -> false
     *
     * @param  {String}  state A value to be compared for valid state
     * @return {Boolean}       True if valid, false otherwise
     */
    function _isValidState(state) {
        return (state == 'az' || state == 'ca' || state == 'ca');
    }

    function _isCookieSet() {
        return ($.cookie(cookieName) && _isValidState($.cookie(cookieName)));
    }

    return {
        // Using a Promise so that multiple calls to _getStateCookie() are handled synchronously
        getStateCookie : function() {
            return _getStateCookie().then( function(state) { return state; });
        },
        setStateCookie : function(state, expirationDate) {
            return _setStateCookie(state, expirationDate);
        },
        updateStateElement : function(target) {
            return _updateStateElement(target);
        },
        isValidState : function(state) {
            return _isValidState(state);
        },
        isCookieSet : function() {
            return _isCookieSet();
        }
    };
})();
<script src="https://raw.githubusercontent.com/carhartl/jquery-cookie/master/src/jquery.cookie.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

を使用して Cookie の値を取得しようとして問題が発生した場合myUtilities.stateManager.getStateCookie()。この呼び出しは、最も近い適用可能な州の 2 文字の文字列で返されることを期待しています。代わりに、Promise オブジェクトが返されます。

文字列の代わりに Promise が返されるのはなぜですか? また、目的の文字列を返すために何を変更する必要がありますか?

お時間をいただきありがとうございます。

4

2 に答える 2

1

残念ながら、javascript の非同期プロセスから同期結果が得られるとは期待できません。あなたが何をしても、非同期が同期に変換されることはありません。(近い将来のある日) 期待できる最善の方法は、非同期コードをより同期のように見せる構文です。

ここにいくつかの提案があります...

では_getLocation()、次のことを行います。

  • 失敗ハンドラーを追加して、jQuery.ajax のエラー報告を単一の理由に正規化します。
function _getLocation(position) {
    var latitude  = position.coords.latitude,
        longitude = position.coords.longitude;

    return $.ajax({
        url: 'https://maps.googleapis.com/maps/api/geocode/json?latlng=' + latitude + ',' + longitude,
        dataType: "json"
    }).then(null, function(jqXHR, textStatus, errorThrown) {
        return errorThrown;
    });
}

では_deferGetLocation()、次のことを行います。

  • から明示的な promise 構築のアンチパターンを削除し_deferGetLocation()ます。
  • 2つの条件の下で約束を拒否する理由を提供します.
  • 少し整頓。
function _deferGetLocation() {
    var promise;
    if ("geolocation" in navigator) {
        navigator.geolocation.getCurrentPosition(function(position) {
            promise = _getLocation(position).then(function(data) {
                var result = data.results[0],
                    state;
                if (data.length !== 0) {
                    // A for-loop is used because the response changes based on the address that Google API returns (a single search into a specific part of the data Object is not always successful even though the data may be in there)
                    for (var i = 0, len = result.address_components.length; i < len; i++) {
                        if (result.address_components[i].types.indexOf('administrative_area_level_1') >= 0) {
                            state = result.address_components[i].short_name.toLowerCase();
                            break;
                        }
                    }
                }
                return state || $.Deferred().reject('geolocation failed').promise();
            });
        });
    return promise || $.Deferred().reject('browser does not support geolocation').promise();
}

名前を変更したでは、次の_getStateCookie()ことを行います。

  • _getStateCookieAsync()メソッドが promise を返すという消費者への警告として名前を変更します。
  • 明示的なプロミス構築のアンチパターンを一掃して_getStateCookie()単純化します (大変なことです)。
function _getStateCookieAsync() {
    var state = $.cookie(cookieName);
    return (state) ? $.when(state) : _deferGetLocation().then(_setStateCookie);
}

そして、メソッドを公開する return ステートメントでは、次のようにします。

  • 必要なものだけを公開します。すべてのメソッドを公開する義務はありません。
  • 関数名で公開 - 追加の関数ラッパーは必要ありません。
return {
    getStateCookieAsync : _getStateCookieAsync,
    setStateCookie : _setStateCookie, // will it ever be set from outside?
    // updateStateElement : _updateStateElement, // doesn't exist
    isValidState : _isValidState, // probably only of use internally
    isCookieSet : _isCookieSet
};
于 2015-08-21T01:12:00.383 に答える
0

ハンドラーをアタッチすることによってのみ約束外の値を取得し、.then()すべての.then()ハンドラーは非同期で実行されます。

したがって、このコードは機能しません。

getStateCookie : function() {
        return _getStateCookie().then( function(state) { return state; });
},

これは、値が であるプロミスを返すだけですstate。ここでは、.then()ハンドラーはコードに何も追加しないことに注意してください。

結果は非同期です。それを変更することはできません。呼び出し元は非同期として処理する必要があります。これは、呼び出し元がコールバック関数を介して結果を取得することを意味します。そのコールバックは、単純なコールバックまたは promise コールバックにすることができます。すでにプロミスを使用しているため、値を含むプロミスを返すだけで、呼び出し元に独自の.then()ハンドラーを配置させて、独自のコールバック関数に渡された値を取得できるようにするのが最も簡単です。

私はこれを提案します:

getStateCookie : function() {
    return _getStateCookie();
},

そして、呼び出し元は次のように使用します。

myUtilities.stateManager.getStateCookie().then(function(state) {
    // callers code goes here to use state
});
于 2015-08-20T22:56:32.157 に答える