programing

Ajax 콜 진행 상황을 보여주는 가장 좋은 방법은 무엇입니까?

lastcode 2023. 3. 8. 21:13
반응형

Ajax 콜 진행 상황을 보여주는 가장 좋은 방법은 무엇입니까?

데이터베이스 내의 5,000개의 레코드를 갱신하는 Ajax 콜이 있기 때문에 시간이 많이 걸립니다.Ajax의 "Loading image"를 보고 있습니다만, "Updating 50 of 5000"이나 "Updating 200 of 5000" 같은 것을 표시할 수 있는 더 좋은 방법을 찾고 있습니다.

Ajax / jQuery에서 5000개의 다른 투고를 하지 않고 이와 같은 작업을 수행하는 가장 좋은 방법은 무엇입니까?

내가 생각하는 가장 좋은 것은 코메트를 이용하는 것이다.

Comet 스타일의 응용 프로그램에서는 클라이언트가 서버에 데이터를 반복적으로 요청하는 대신 서버가 기본적으로 클라이언트에 데이터를 푸시할 수 있습니다.클라이언트는 서버에 한 번만 연결하면 됩니다.그 후 서버는 데이터를 클라이언트에 계속 푸시합니다.

Wikipedia에서:

Comet은 클라이언트가 데이터를 요구하지 않고 웹 서버가 클라이언트에 데이터를 전송할 수 있도록 하는 프로그래밍 기술입니다.브라우저에서 호스트되는 이벤트 기반 웹 응용 프로그램을 만들 수 있습니다.

이제 코메트가 어떻게 작동하는지 봅시다.에 'a'라는 글자가 있습니다.while루프가 사용되고 있습니다.대신, 독자적인 조건을 설정할 수 있습니다.한편, datetime은 sleep 1/2로 설정되어 있습니다.

ASP.NET 페이지 코드 뒷면:Service.aspx.cs

public static string Delimiter = "|";

protected void Page_Load(object sender, EventArgs e)
{
    Response.Buffer = false;

    while (true)
    {
        Response.Write(Delimiter
            + DateTime.Now.ToString("HH:mm:ss.FFF"));
        Response.Flush();

        // Suspend the thread for 1/2 a second
        System.Threading.Thread.Sleep(500);
    }

    // Yes I know we'll never get here,
    // it's just hard not to include it!
    Response.End();
}

클라이언트 측 코드 - 순수 JavaScript

번만 하세요.readyState === 3XMLHttpRequest.

function getData()
{
    loadXMLDoc("Service.aspx");
}

var req = false;
function createRequest() {
    req = new XMLHttpRequest(); // http://msdn.microsoft.com/en-us/library/ms535874%28v=vs.85%29.aspx
}

function loadXMLDoc(url) {
    try {
        if (req) { req.abort(); req = false; }

        createRequest();

        if (req) {
            req.onreadystatechange = processReqChange;
            req.open("GET", url, true);
            req.send("");
        }
        else { alert('unable to create request'); }
    }
    catch (e) { alert(e.message); }
}

function processReqChange() {
    if (req.readyState == 3) {
        try {
            ProcessInput(req.responseText);

            // At some (artibrary) length   recycle the connection
            if (req.responseText.length > 3000) { lastDelimiterPosition = -1; getData(); }
        }
        catch (e) { alert(e.message); }
    }
}

var lastDelimiterPosition = -1;
function ProcessInput(input) {
    // Make a copy of the input
    var text = input;

    // Search for the last instance of the delimiter
    var nextDelimiter = text.indexOf('|', lastDelimiterPosition + 1);
    if (nextDelimiter != -1) {

        // Pull out the latest message
        var timeStamp = text.substring(nextDelimiter + 1);
        if (timeStamp.length > 0) {
            lastDelimiterPosition = nextDelimiter;
            document.getElementById('outputZone').innerHTML = timeStamp;
        }
    }
}

window.onload = function () { getData(); };

언급

SESSION 변수에서 빅업데이트를 하고 있는 함수가 각각의 단일 업데이트(또는 다수의 업데이트) 후 현재 진행상황을 기록하도록 하고 별도의 AJAX 스크립트를 사용하여 SESSION에서 이 진행률 값을 가져오고 JavaScript가 이를 사용하여 진행률 바/텍스트를 업데이트하도록 합니다.

현재 배치 업데이트의 모든 레코드에 대해 1개의 POST를 사용하고 있으며, 콜과 리턴 사이에 로드 이미지를 배치하고 있다고 가정합니다.

업데이트가 완료될 때까지 서버가 반환되기를 기다리는 대신 해당 배치 업데이트에 대한 특수 ID를 사용하여 즉시 반환하도록 하십시오.그런 다음 배치 업데이트 상태를 반환하는 서버 호출을 구현합니다. 이 상태는 진행 상황 대화 상자에서 호출하여 진행 상황을 보고할 수 있습니다.

var progressCallback = function(data){
  //update progress dialog with data
  //if data does not indicate completion
    //ajax call status function with "progressCallback" as callback
});
//ajax call status function with "progressCallback" as callback

어느 정도(업데이트된 레코드 수 등)가 완료되었는지 쿼리할 수 있는Ajax 콜백을 n밀리초마다 1회 실행하고 이를 사용하여 진행률 바를 표시합니다.이런 식으로요

방금 댓글을 읽다가 생각이 났어요.

JavaScript와 PHP는 쿠키를 공유하며,

  1. Ajax 호출 시 JavaScript를 사용하여 쿠키를 만듭니다.
  2. Ajax PHP 파일에서는 SQL을 업데이트할 때마다 쿠키의 값이 증가합니다.
  3. JavaScript에서 재귀 함수는 특정 쿠키를 읽고 진행률 바 번호를 업데이트합니다.

장점:

  1. Ajax 콜은 1개뿐입니다.
  2. 서버의 부하가 경감됩니다.

응답 버퍼를 업데이트하여 서버에서 주기적으로 응답 버퍼를 플러시할 수 있습니다.

이 수 .xhttpr. " streaming할 수 iframe은 http streaming을 통해 처리됩니다.

하지만 그것조차도 대략적일 수 있다.HTTP는 단편적으로 또는 단편적으로 전송하기 위한 것이 아닙니다.다른 사람의 지적과 같이, 동작의 상태를 취득하기 위해서 다른 콜을 발신하는 것이 최선입니다.

단순히 SQL 문을 실행하는 것이 아니라 각 레코드를 개별적으로 반복해야 하는 이유가 있다고 생각합니다.

이 경우 200회 정도 반복할 때마다 Ajax 콜을 발신하기만 하면 됩니다.200 레코드의 각 그룹에 대해 실행할 경우 소비되는 Ajax 콜은 50개뿐입니다.

(의사코드):

If iterationNumber mod 200 == 0
    // Make Ajax call.

투고처의서버측을잘모르겠습니다만, 이 방법을 대부분의 프로그래밍 언어에 적용할 수 있습니다.예를 들어 PHP를 사용하고 있습니다.

HTML 측에서는 진행률 표시줄을 지정된 너비로 업데이트하는 기능이 있습니다.이 예에서는 이 함수를 'setProgress'라고 부릅니다.이 함수는 진행률 바를 업데이트하기 위해 번호가 필요합니다.

서버측 코드에서 업데이트 청크(반복당 100 등)를 수행하여 출력을 생성합니다.각 반복에 대해 javascript 호출을 출력하면 다음과 같이 됩니다.

<?php
  while () { // Whatever your loop needs to be.
  // Do your chunk of updates here.
?>
   <script type="text/javascript">
     setProgress(100);
   </script>
<?php
    flush(); // Send what we have so far to the browser so the script is executed.
  }
  setProgress(5000); // All done!
?>

에코 후 출력 버퍼를 플러시하여 이 데이터가 브라우저로 전송되는지 확인합니다.완전한 스크립트 태그이기 때문에 브라우저는 내부에서 Javascript를 실행하고 진행 표시줄을 사용자가 전달한 값으로 업데이트합니다.

이 모든 것이 작동하려면 프로그레스 바에 호출하는 번호를 이해하기 위해 몇 가지 계산을 추가해야 합니다만, 큰 문제는 없을 것으로 생각합니다.예를 들어 사용률에 set Progress를 사용하는 것이 더 타당할 수 있습니다.단, 명확성을 위해 필요한 계산을 피하고 싶었습니다.

다음과 같은 간단한 테이블을 만듭니다.

CREATE TABLE progress_data (
  statusId int(4) NOT NULL AUTO_INCREMENT,
  progress float DEFAULT NULL COMMENT 'percentage',
  PRIMARY KEY (id_progress_data)
);

JQuery 코드:

//this uses Jquery Timers http://plugins.jquery.com/project/timers
$('#bUpdate').click(function() {
    //first obtain a unique ID of this operation - this has to by synchronized
    $.ajaxSetup({'async': false});
    $.post('ajax.php', {'operation': 'beginOperation'}, function(data) {
        statusId = parseInt(data.statusId);
    });
    //now run the long-running task with the operation ID and other params as necessary
    $.ajaxSetup({'async': true});
    $.post('ajax.php', {'operation': 'updateSite', 'statusId': statusId, 'param': paramValue}, function(data) {
        $('#progress_bar').stopTime('statusLog'); //long operation is finished - stop the timer
        if (data.result) {
            //operation probably successful
        } else {
            //operation failed
        }
    });
    //query for progress every 4s, 'statusLog' is just the name of the timer
    $('#progress_bar').everyTime('4s', 'statusLog', function() {
        var elm = $(this);
        $.post('ajax.php', {'operation': 'showLog', 'statusId': statusId}, function(data) {
            if (data) {
                //set bar percentage
                $('#progress').css('width', parseInt(data.progress) + '%');
            }
        });
    });
    return false;
}

백엔드 코드(PHP):

if (isset($_POST['operation'])) {
        ini_set("display_errors", false);
        session_write_close();  //otherwise requests would block each other
        switch ($_POST['operation']) {
            /**
            * Initialize progress operation, acquire ID (statusId) of that operation and pass it back to
            *   JS frontend. The frontend then sends the statusId back to get current state of progress of
            * a given operation.
            */
            case 'beginOperation': {
                $statusId = //insert into progress_data
                echo json_encode(array('statusId' => $statusId));
                break;
            }
            /**
            * Return back current progress state.
            */
            case 'showLog': {
                $result->progress = (float) //SELECT progress FROM progress_data WHERE statusId = $_POST['statusId']
                echo json_encode($result);
                break;
            }
            case 'updateSite': {
                //start long running operation, return whatever you want to, during the operation ocassionally do:
                    UPDATE progress_data SET progress=... WHERE statusId = $_POST['statusId']
            }
        }
    }
    /* Terminate script, since this 'view' has no template, there si nothing to display.
    */
    exit;

이 접근방식은 이미 3개의 어플리케이션에서 사용하고 있으며 매우 신뢰성이 높고 고속입니다(showLog 조작은 단순한 SELECT 문일 뿐입니다).세션을 사용하여 진행 상황을 저장할 수도 있지만 세션이 쓰기 닫힘(파일에 저장되는 경우)이 필요하기 때문에 많은 문제가 발생합니다.그렇지 않으면 showLog AJAX 쿼리는 긴 조작이 완료될 때까지 대기합니다(및 루즈 센스).

뭔가 수상쩍다.

5000개의 레코드를 순식간에 갱신할 수 없는 데이터베이스는 잘 모르는데...즉, 이것은 단순히 하나의 업데이트가 아니라 레코드 업데이트에 의한 기록이라는 것입니까?

사용자가 5000개의 엔트리를 다운로드하여 편집한 엔트리를 표시하지 않고 업데이트 마지막에 단일 업데이트버튼을 적용하면 5000개의 레코드가 모두 반환됩니다.그것은 최악의 경우일 것이다.

따라서 대기 시간이 발생하지 않도록 문제를 분할할 수 있는 방법이 있을 수 있습니다.예를 들어 임시 DB 테이블(또는 애플리케이션 메모리 내에서만 ORM 엔티티 목록을 작성하는 것만으로 매우 간단함)을 고려해 보십시오.단, 그건 별개입니다).이러한 갱신을 커밋하는 경우에는 클라이언트/서버의 전송 없이 적어도 일괄적으로 갱신을 실시할 수 있습니다.편집된 개별 필드를 표시하여 정확히 변경된 내용을 제외하고 DB에서 더 이상 업데이트되지 않도록 할 수도 있습니다.

오랜 시간이 걸리는 과정들이 있고, 때때로 누군가가 당신에게 제공한 것을 사용해야만 하는 상황에 처할 수도 있다는 것을 알고 있습니다.하지만 약간의 생각만 하면 대기시간을 없앨 수 있을 것이다.

저도 이와 비슷한 일을 한 적이 있습니다(Jain Shaikh와 비슷하지만 단순합니다).

서버상

int toUpdate = 5000;  
int updated = 0;  
int prev = updated;
while(updated < toUpdate)  
{  
    updated = getAlreadyUpdatedRows();
    flushToClient(generateZeroSequenceOfLength(updated - prev));  
    prev = updated;  
    sleep(500);  
}  
closeStream();

클라이언트 상
자인 샤이크 길을 따라가되ProcessInput갱신할 레코드 수와 갱신할 레코드 수의 비율에 따라 진행률 바의 크기를 조정하기만 하면 됩니다.input.length.

이 솔루션에서는 클라이언트 측의 복잡성과 네트워크 대역폭을 교환하는 경우가 많습니다.

실제로 무엇을 하고 있는지 모르는 경우, 이 서버 액티비티를 최종 파일 Import와 혼재시키지 마십시오.안정성을 위해 db 쿼리(업데이트된 행을 카운트하기 위한 선택 항목)를 거래합니다.사용자가 페이지를 변경하면 어떻게 됩니까?프로그레스바에는 문제가 없지만 Import가 완료되지 않습니다.

로드 시 진행 상황을 표시하기 위해 백엔드를 수정하여 선택적인 로드를 할 수 있도록 하겠습니다.

예를들면,

var total_rec = 5000;
var load_steps = 20;
var per_load = total_rev / load_steps;
var loaded = 0; 
while (loaded < total_rec) {
    www.foobar.com/api.php?start=loaded&end=(loaded+per_load);
    loaded += per_load;
}

로드가 완료될 때마다 진행 표시줄을 업데이트합니다.

백엔드를 변경하는 대체 방법은 다음과 같습니다.

www.foobar.com/api.php?start=loaded&count=50

언급URL : https://stackoverflow.com/questions/3901495/what-is-the-best-way-of-showing-progress-on-an-ajax-call

반응형