Как использовать jQuery promise/deffered в пользовательской функции?

У меня есть функция, которая получает местоположение через navigator.geolocation:

var getLocation = function( callback ){

    navigator.geolocation.getCurrentPosition( callback || function( position ){

        // Stuff with geolocation

    });

};

Я хотел бы сделать так, чтобы я мог связать эту функцию с помощью объекта jQuerys Deffered но мне до сих пор не удалось понять концепцию и использование Deffered.

Я ищу что-то похожее на этот псевдокод:

getLocation().then(function(){
    drawMarkerOnMap();
});

Возможен ли вообще такой синтаксис без переворачивания назад и утопления в коде?


person hitautodestruct    schedule 17.01.2013    source источник


Ответы (3)


Вы должны создать экземпляр нового отложенного объекта и вернуть его (или его обещание) из функции. Вызовите его метод .resolve, как только получите ответ:

var getLocation = function() {
    var deferred = new $.Deferred();

    navigator.geolocation.getCurrentPosition(function( position ){
        // Stuff with geolocation
        deferred.resolve(position);
    });

    // return promise so that outside code cannot reject/resolve the deferred
    return deferred.promise();
};

Применение:

getLocation().then(drawMarkerOnMap);

Ссылка: jQuery.Deferred


Дополнение:

Я бы посоветовал не использовать оба подхода, отложенные объекты и передачу обратных вызовов в функцию, чтобы интерфейс оставался простым. Но если вам нужно сохранить обратную совместимость, вы можете просто зарегистрировать переданный обратный вызов в отложенном объекте:

var getLocation = function(callback) {
    var deferred = new $.Deferred();

    if ($.isFunction(callback)) {
        deferred.then(callback);
    }

    navigator.geolocation.getCurrentPosition(function( position ){
        // Stuff with geolocation
        deferred.resolve(position);
    });

    // return promise so that outside code cannot reject/resolve the deferred
    return deferred.promise();
};
person Felix Kling    schedule 17.01.2013
comment
Спасибо за ответ и фантастический ответ! Когда вы говорите ... совет против использования обоих подходов, отложенных объектов и передачи обратных вызовов ... как еще вы могли бы поддерживать асинхронный запрос, такой как getCurrentPosition? - person hitautodestruct; 17.01.2013
comment
@hitautodestruct: Нет, я имел в виду, что getLocation принимает обратный вызов и возвращает отложенный объект. т.е. было бы два пути, либо foo(bar), либо foo().then(bar). Должен быть только один способ вызвать эту функцию. - person Felix Kling; 17.01.2013
comment
Посмотрите этот ответ, если вы не используете/не нуждаетесь в jquery - person hitautodestruct; 20.03.2019

Несмотря на то, что приведенный выше пример действительно помог мне, мне пришлось немного больше прочитать, чтобы понять концепцию.

Ниже приведен пример, основанный на моем коде, который содержит комментарии, которые помогут мне, когда я вернусь к нему, и, надеюсь, всем, кто читает этот вопрос о Stackoverflow:

/* promise based getFilter to accommodate getting surrounding suburbs */
oSearchResult.fPromiseOfFilterSetting = function fPromiseOfFilterSetting(sId) {
    var self = this;
    self.oPromiseCache = self.oPromiseCache || {}; // creates a persistent cache 
                                                   // across function calls
    var oDeferred = $.Deferred(); // `new` keyword is optional
    var oPromise = oDeferred.promise();

    // leverage the cache (it's ok if promise is still pending), you can key
    if (self.oPromiseCache[sId] !== undefined) {
        return self.oPromiseCache[sId];
    }
    else {
        self.oPromiseCache[sId] = oPromise;
    }

    // do our asynchronous action below which at some point calls
    // defered.resolve(...) and hence complete our promise
    $.cmsRestProxy.doAjaxServiceRequest('ocms_searchProperties_Extension', {
        action : 'getSurroundingSuburbs',
        sSuburbIds : 'a0RO0000003BwWeMAK'
    }, function(result, json) {
        console.log("doAjaxServiceRequest(
                       'ocms_searchProperties_Extension')", json);
        oDeferred.resolve(json); // `json` is our result and `.resolve(json)` 
                                 // passes the value as first argument to 
                                 // the `oPromise.done`, `oPromise.fail` 
                                 // and `oPromise.always` callback functions
    })

    // We can now return the promise or attach optional `oPromise.done`,
    // `oPromise.fail`, and `oPromise.always` callbacks which will execute first
    // in the chain.
    //
    // Note that `oPromise.then(doneCallback, failCallback, alwaysCallback)`
    // is short form for the below
    oPromise.done(function(value) { // returned by promise.resolve(...); call
        console.log('will run if this Promise is resolved.', value);
    })
    oPromise.fail(function(value) {
        console.log("will run if this Promise is rejected.", value);
    });
    oPromise.always(function(value) {
        console.log("this will run either way.", value);
    });

    // return a promise instead of deferred object so that
    // outside code cannot reject/resolve it
    return oPromise;
}

// then to use one would do
oSearchResult.fPromiseOfFilterSetting().done(function(value) {alert(value)});

// or using $.when chaining
$.when(
    oSearchResult.fPromiseOfFilterSetting()
)
.done(
      function fDoneCallback(arg1, arg2, argN) {
          console.debug(arguments) // `arguments` is an array of all args collected
      }
);
person Daniel Sokolowski    schedule 19.06.2014
comment
Не могли бы вы использовать navigator.geolocation, как указано в вопросе? В противном случае это может быть хорошо прокомментированный фрагмент кода, но не ответ. - person Bergi; 15.07.2014
comment
Нет, oPromise.then(…) не эквивалентно oPromise.done().fail().always(). Последний обратный вызов предназначен для событий progress! - person Bergi; 15.07.2014
comment
И это даже не эквивалентно oPromise.done().fail().progress(). Метод then является основой для монадная функциональность промисов - person Bergi; 15.07.2014
comment
Обратите внимание, что вы не должны использовать $.when, если знаете, что аргумент является обещанием. - person Bergi; 15.07.2014

Я знаю, что в заголовке написано jQuery, но когда я задал этот вопрос, промисы были новинкой для Интернета, а jQuery была библиотекой де-факто. Вот более современный ответ без jQuery.

Используйте собственный Promise

Все современные браузеры (кроме IE11 и ниже; при необходимости используйте полифилл), позволяют использовать нативную конструкцию Promise.

let getLocation = () => {

  return new Promise( ( resolve, reject ) => {

    try {
      navigator.geolocation.getCurrentPosition( position => {
        resolve( position )
      })
    } catch ( err ) {
      reject( err )
    }

  })

};

Применение:

let runGetLocation = () => { getLocation().then( position => console.log( position ) ) }

Вы также можете использовать async/await ES2016 вместо .then():

let runGetLocation = async () => {

  try {
    let position = await getLocation()
    console.log( position )
  } catch ( err ) { console.log( err ) }

}
person hitautodestruct    schedule 14.10.2018