IT

jQuery 지연을 어떻게 사용할 수 있습니까?

lottoking 2020. 3. 24. 00:00
반응형

jQuery 지연을 어떻게 사용할 수 있습니까?


jQuery를 1.5은 새로운 이연 객체와 연결 방법을 제공합니다 .when, .Deferred._Deferred.

.Deferred이전에 사용하지 않은 사람들을 위해 소스에 주석을 달았 습니다 .

이 새로운 방법의 가능한 사용법은 무엇입니까? 패턴에 맞추는 방법은 무엇입니까?

API소스를 이미 읽었 으므로 그 기능을 알고 있습니다. 내 질문은 일상적인 코드에서 이러한 새로운 기능을 어떻게 사용할 수 있습니까?

AJAX 요청을 순서대로 호출하는 버퍼 클래스 의 간단한 예가 있습니다. (이전 단계가 끝나면 다음 단계가 시작됩니다).

/* Class: Buffer
 *  methods: append
 *
 *  Constructor: takes a function which will be the task handler to be called
 *
 *  .append appends a task to the buffer. Buffer will only call a task when the 
 *  previous task has finished
 */
var Buffer = function(handler) {
    var tasks = [];
    // empty resolved deferred object
    var deferred = $.when();

    // handle the next object
    function handleNextTask() {
        // if the current deferred task has resolved and there are more tasks
        if (deferred.isResolved() && tasks.length > 0) {
            // grab a task
            var task = tasks.shift();
            // set the deferred to be deferred returned from the handler
            deferred = handler(task);
            // if its not a deferred object then set it to be an empty deferred object
            if (!(deferred && deferred.promise)) {
                deferred = $.when();
            }
            // if we have tasks left then handle the next one when the current one 
            // is done.
            if (tasks.length > 0) {
                deferred.done(handleNextTask);
            }
        }
    }

    // appends a task.
    this.append = function(task) {
        // add to the array
        tasks.push(task);
        // handle the next task
        handleNextTask();
    };
};

나는 시위와의 가능한 사용을 찾고 있어요 .Deferred.when.

의 예제를 보는 것도 좋을 것입니다 ._Deferred.

jQuery.ajax예를 들어 새로운 소스에 연결하는 것은 부정 행위입니다.

작업이 동 기적으로 수행되는지 비동기 적으로 수행되는지 추상화 할 때 어떤 기술을 사용할 수 있는지에 특히 관심이 있습니다.


내가 생각할 수있는 가장 좋은 사용 사례는 AJAX 응답을 캐싱하는 것입니다. 다음 은 주제에 대한 Rebecca Murphey의 소개 게시물 에서 수정 된 예입니다 .

var cache = {};

function getData( val ){

    // return either the cached value or jqXHR object wrapped Promise
    return $.when(
        cache[ val ] || 
        $.ajax('/foo/', {
            data: { value: val },
            dataType: 'json',
            success: function( resp ){
                cache[ val ] = resp;
            }
        })
    );
}

getData('foo').then(function(resp){
    // do something with the response, which may
    // or may not have been retrieved using an
    // XHR request.
});

기본적으로 값이 이미 한 번 요청 된 경우 캐시에서 즉시 반환됩니다. 그렇지 않으면 AJAX 요청이 데이터를 가져 와서 캐시에 추가합니다. $.when/는 .then이 중 하나에 대해 상관하지 않는다; .then()두 가지 경우 모두 처리기에 전달되는 응답을 사용하기 만하면됩니다. jQuery.when()비 약속 / 지연을 완료된 것으로 처리하여 즉시 .done()또는 .then()체인에서 실행합니다 .

지연은 작업이 비동기 적으로 작동하거나 작동하지 않을 때 적합하며 코드에서 해당 조건을 추상화하려고합니다.

$.when도우미 를 사용하는 또 다른 실제 예 :

$.when($.getJSON('/some/data/'), $.get('template.tpl')).then(function (data, tmpl) {

    $(tmpl) // create a jQuery object out of the template
    .tmpl(data) // compile it
    .appendTo("#target"); // insert it into the DOM

});

다음은 ehynd 's answer 에서와 같이 AJAX 캐시를 약간 다르게 구현 한 입니다.

fortuneRice의 후속 질문 에서 언급했듯이 ehynd의 구현은 요청 중 하나가 반환되기 전에 요청이 수행 된 경우 실제로 동일한 요청을 여러 번 방지하지 못했습니다. 그건,

for (var i=0; i<3; i++) {
    getData("xxx");
}

"xxx"에 대한 결과가 이전에 캐시되지 않은 경우 3 개의 AJAX 요청이 발생합니다.

결과 대신 요청의 지연을 캐싱하여 해결할 수 있습니다.

var cache = {};

function getData( val ){

    // Return a promise from the cache (if available)
    // or create a new one (a jqXHR object) and store it in the cache.
    var promise = cache[val];
    if (!promise) {
        promise = $.ajax('/foo/', {
            data: { value: val },
            dataType: 'json'
        });
        cache[val] = promise;
    }
    return promise;
}

$.when(getData('foo')).then(function(resp){
    // do something with the response, which may
    // or may not have been retreived using an
    // XHR request.
});

뮤텍스 대신에 지연을 사용할 수 있습니다. 이것은 본질적으로 여러 ajax 사용 시나리오와 동일합니다.

뮤텍스

var mutex = 2;

setTimeout(function() {
 callback();
}, 800);

setTimeout(function() {
 callback();
}, 500);

function callback() {
 if (--mutex === 0) {
  //run code
 }
}

지연됨

function timeout(x) {
 var dfd = jQuery.Deferred();
 setTimeout(function() {
  dfd.resolve();
 }, x);
 return dfd.promise();
}

jQuery.when(
timeout(800), timeout(500)).done(function() {
 // run code
});

Deferred를 뮤텍스로만 사용하는 경우 성능 영향을주의하십시오 (http://jsperf.com/deferred-vs-mutex/2). Deferred가 제공하는 편의성과 추가 혜택은 그만한 가치가 있지만 실제 (사용자 중심 이벤트 기반) 사용에서는 성능 영향이 눈에 띄지 않아야합니다.


이것은 자체 판촉적인 답변이지만 몇 달 동안 이것을 연구하고 jQuery Conference San Francisco 2012에서 결과를 발표했습니다.

다음은이 대화의 무료 비디오입니다.

https://www.youtube.com/watch?v=juRtEEsHI9E


내가 좋은 목적으로 사용했던 또 다른 용도는 여러 소스에서 데이터를 가져 오는 것입니다. 아래 예에서는 클라이언트와 REST 서버 간의 유효성 검사를 위해 기존 애플리케이션에서 사용되는 여러 개의 독립적 인 JSON 스키마 객체를 가져옵니다. 이 경우 브라우저 측 응용 프로그램이 모든 스키마를로드하기 전에 데이터로드를 시작하지 않으려합니다. $ .when.apply (). then ()은 이것에 완벽합니다. then (fn1, fn2)을 사용하여 오류 조건을 모니터링하는 방법에 대한 포인터는 Raynos에게 감사하십시오.

fetch_sources = function (schema_urls) {
    var fetch_one = function (url) {
            return $.ajax({
                url: url,
                data: {},
                contentType: "application/json; charset=utf-8",
                dataType: "json",
            });
        }
    return $.map(schema_urls, fetch_one);
}

var promises = fetch_sources(data['schemas']);
$.when.apply(null, promises).then(

function () {
    var schemas = $.map(arguments, function (a) {
        return a[0]
    });
    start_application(schemas);
}, function () {
    console.log("FAIL", this, arguments);
});     

Deferreds를 사용하여 모든 종류의 계산 (일반적으로 성능 집약적이거나 오래 실행되는 작업)에 대한 캐시를 구현하는 또 다른 예 :

var ResultsCache = function(computationFunction, cacheKeyGenerator) {
    this._cache = {};
    this._computationFunction = computationFunction;
    if (cacheKeyGenerator)
        this._cacheKeyGenerator = cacheKeyGenerator;
};

ResultsCache.prototype.compute = function() {
    // try to retrieve computation from cache
    var cacheKey = this._cacheKeyGenerator.apply(this, arguments);
    var promise = this._cache[cacheKey];

    // if not yet cached: start computation and store promise in cache 
    if (!promise) {
        var deferred = $.Deferred();
        promise = deferred.promise();
        this._cache[cacheKey] = promise;

        // perform the computation
        var args = Array.prototype.slice.call(arguments);
        args.push(deferred.resolve);
        this._computationFunction.apply(null, args);
    }

    return promise;
};

// Default cache key generator (works with Booleans, Strings, Numbers and Dates)
// You will need to create your own key generator if you work with Arrays etc.
ResultsCache.prototype._cacheKeyGenerator = function(args) {
    return Array.prototype.slice.call(arguments).join("|");
};

다음은이 클래스를 사용하여 시뮬레이션 된 무거운 계산을 수행하는 예입니다.

// The addingMachine will add two numbers
var addingMachine = new ResultsCache(function(a, b, resultHandler) {
    console.log("Performing computation: adding " + a + " and " + b);
    // simulate rather long calculation time by using a 1s timeout
    setTimeout(function() {
        var result = a + b;
        resultHandler(result);
    }, 1000);
});

addingMachine.compute(2, 4).then(function(result) {
    console.log("result: " + result);
});

addingMachine.compute(1, 1).then(function(result) {
    console.log("result: " + result);
});

// cached result will be used
addingMachine.compute(2, 4).then(function(result) {
    console.log("result: " + result);
});

동일한 기본 캐시를 사용하여 Ajax 요청을 캐시 할 수 있습니다.

var ajaxCache = new ResultsCache(function(id, resultHandler) {
    console.log("Performing Ajax request for id '" + id + "'");
    $.getJSON('http://jsfiddle.net/echo/jsonp/?callback=?', {value: id}, function(data) {
        resultHandler(data.value);
    });
});

ajaxCache.compute("anID").then(function(result) {
    console.log("result: " + result);
});

ajaxCache.compute("anotherID").then(function(result) {
    console.log("result: " + result);
});

// cached result will be used
ajaxCache.compute("anID").then(function(result) {
    console.log("result: " + result);
});

이 jsFiddle 에서 위의 코드로 재생할 수 있습니다 .


1) 콜백을 순서대로 실행하려면 사용하십시오.

var step1 = new Deferred();
var step2 = new Deferred().done(function() { return step1 });
var step3 = new Deferred().done(function() { return step2 });

step1.done(function() { alert("Step 1") });
step2.done(function() { alert("Step 2") });
step3.done(function() { alert("All done") });
//now the 3 alerts will also be fired in order of 1,2,3
//no matter which Deferred gets resolved first.

step2.resolve();
step3.resolve();
step1.resolve();

2) 앱 상태를 확인하는 데 사용하십시오.

var loggedIn = logUserInNow(); //deferred
var databaseReady = openDatabaseNow(); //deferred

jQuery.when(loggedIn, databaseReady).then(function() {
  //do something
});

지연된 객체를 사용하여 웹킷 브라우저에서 잘 작동하는 유동적 인 디자인을 만들 수 있습니다. 웹킷 브라우저는 각 크기 조정에 대해 이벤트를 한 번만 발생시키는 FF 및 IE와 달리 창의 크기가 조정되는 각 픽셀에 대해 크기 조정 이벤트를 발생시킵니다. 결과적으로, 창 크기 조정 이벤트에 바인드 된 함수가 실행될 순서를 제어 할 수 없습니다. 이와 같은 것이 문제를 해결합니다.

var resizeQueue = new $.Deferred(); //new is optional but it sure is descriptive
resizeQueue.resolve();

function resizeAlgorithm() {
//some resize code here
}

$(window).resize(function() {
    resizeQueue.done(resizeAlgorithm);
});

그러면 코드 실행이 직렬화되어 의도 한대로 실행됩니다. 객체 메소드를 콜백으로 연기 할 때 함정에주의하십시오. 이러한 메소드가 지연된 콜백으로 실행되면 지연된 오브젝트를 참조하여 'this'참조가 겹쳐 쓰여지고 메소드가 속한 오브젝트를 더 이상 참조하지 않습니다.


JQuery를 사용하는 타사 라이브러리와 통합 할 수도 있습니다.

그러한 라이브러리 중 하나는 Backbone으로, 실제로 다음 버전에서 Deferred를 지원할 것입니다.


방금 실제 코드에서 Deferred를 사용했습니다. 프로젝트 jQuery 터미널 에서 사용자가 정의한 명령을 호출하는 함수 exec가 있습니다 (예 : 입력하고 Enter 키를 누름). API에 Deferreds를 추가하고 배열로 exec를 호출했습니다. 이처럼 :

terminal.exec('command').then(function() {
   terminal.echo('command finished');
});

또는

terminal.exec(['command 1', 'command 2', 'command 3']).then(function() {
   terminal.echo('all commands finished');
});

명령은 비동기 코드를 실행할 수 있으며 exec는 사용자 코드를 순서대로 호출해야합니다. 내 첫 API는 일시 중지 / 재개 호출 쌍을 사용하고 새로운 API에서는 사용자가 약속을 반환 할 때 자동으로 호출합니다. 따라서 사용자 코드는

return $.get('/some/url');

또는

var d = new $.Deferred();
setTimeout(function() {
    d.resolve("Hello Deferred"); // resolve value will be echoed
}, 500);
return d.promise();

나는 다음과 같은 코드를 사용한다 :

exec: function(command, silent, deferred) {
    var d;
    if ($.isArray(command)) {
        return $.when.apply($, $.map(command, function(command) {
            return self.exec(command, silent);
        }));
    }
    // both commands executed here (resume will call Term::exec)
    if (paused) {
        // delay command multiple time
        d = deferred || new $.Deferred();
        dalyed_commands.push([command, silent, d]);
        return d.promise();
    } else {
        // commands may return promise from user code
        // it will resolve exec promise when user promise
        // is resolved
        var ret = commands(command, silent, true, deferred);
        if (!ret) {
            if (deferred) {
                deferred.resolve(self);
                return deferred.promise();
            } else {
                d = new $.Deferred();
                ret = d.promise();
                ret.resolve();
            }
        }
        return ret;
    }
},

dalyed_commands는 모든 dalyed_commands와 함께 exec를 다시 호출하는 이력서 기능에 사용됩니다.

명령 기능의 일부 (관련 부품을 제거하지 않았습니다)

function commands(command, silent, exec, deferred) {

    var position = lines.length-1;
    // Call user interpreter function
    var result = interpreter.interpreter(command, self);
    // user code can return a promise
    if (result != undefined) {
        // new API - auto pause/resume when using promises
        self.pause();
        return $.when(result).then(function(result) {
            // don't echo result if user echo something
            if (result && position === lines.length-1) {
                display_object(result);
            }
            // resolve promise from exec. This will fire
            // code if used terminal::exec('command').then
            if (deferred) {
                deferred.resolve();
            }
            self.resume();
        });
    }
    // this is old API
    // if command call pause - wait until resume
    if (paused) {
        self.bind('resume.command', function() {
            // exec with resume/pause in user code
            if (deferred) {
                deferred.resolve();
            }
            self.unbind('resume.command');
        });
    } else {
        // this should not happen
        if (deferred) {
            deferred.resolve();
        }
    }
}

ehynds의 답변은 응답 데이터를 캐시하기 때문에 작동하지 않습니다. 또한 약속 인 jqXHR을 캐시해야합니다. 올바른 코드는 다음과 같습니다.

var cache = {};

function getData( val ){

    // return either the cached value or an
    // jqXHR object (which contains a promise)
    return cache[ val ] || $.ajax('/foo/', {
        data: { value: val },
        dataType: 'json',
        success: function(data, textStatus, jqXHR){
            cache[ val ] = jqXHR;
        }
    });
}

getData('foo').then(function(resp){
    // do something with the response, which may
    // or may not have been retreived using an
    // XHR request.
});

Julian D.의 답변이 올바르게 작동하고 더 나은 솔루션입니다.

참고 URL : https://stackoverflow.com/questions/4869609/how-can-jquery-deferred-be-eded

반응형