2012-02-16 103 views
7

我正在研究使用ajax請求和會話變量更新進度的進度條。當我的程序執行耗時的操作時,比如發送很多電子郵件等,它只需設置適當的會話變量(包含進度值)。此操作由以下代碼中的函數post()開始。PHP和Ajax進度條

與此同時,第二個函數ask()每500ms循環執行一次。它應該實時返回當前的進度。問題在於:ask()發送的每個請求都在等待post()函數發送的請求完成。有趣的是,如果我設置一些URL,如google.com而不是url/to/progress,它的工作原理很好,除非它不是我想要的:)。這意味着問題出在服務器端。

不確定它是否重要,但我使用Yii Framework。

下面的所有代碼只是從頭開始(但工作),它的唯一目的是顯示我的意思。

在此先感謝。

對不起,我的英文不好:)

查看部分:

<script type="text/javascript"> 
function ask() { 
    var d = new Date(); 
    var time = d.getTime(); 
    $.ajax({ 

    type: 'get', 
    url: '/url/to/progress' + '?time=' + time, 
    success: function(data) { 
     $("#progress").html(data); 
    } 
    }) 
} 

function post() { 
    var d = new Date(); 
    var time = d.getTime(); 

    $.ajax({ 
     type: 'post', 
     url: '/url/to/post' + '?time=' + time, 
     data: {"some": "data"}, 
     success: function(data) {alert(data)} 
    }); 
} 

$("#test").click(
    function() { 
    post(); 
    var progress = setInterval("ask();", 500); 
    } 
); 
</script> 

控制器部分:

public function actionPost($time) { 
    sleep(5); // time consuming operation 
    echo $time . ' : ' . microtime(); 
    exit; 
} 

public function actionProgress($time) { 
    echo $time . ' : ' . microtime(); 
    exit; 
} 
+0

你從服務器獲得的迴應是什麼?檢查開發工具>網絡> xhr請求 – 2012-02-16 14:07:04

回答

9

我想在這裏你的問題是會話相關的。

當腳本有一個打開的會話時,它會鎖定會話文件。這意味着任何使用相同會話ID的後續請求都將排隊,直到第一個腳本釋放它對會話文件的鎖定。您可以用session_write_close()強制執行此操作 - 但這不會對您有所幫助,因爲您正嘗試與會話文件共享進度信息,因此post腳本需要保持會話數據的可打開和可寫。

您將需要拿出postprogress腳本之間共享數據的另一種方式 - 如果post擁有遍佈它的執行開放會話數據,progress將永遠無法訪問會話數據,直到後post執行完畢。也許你可以使用會話ID來創建一個臨時文件,其中post有寫入訪問權限,其中可以放入進度指示器數據。 progress可以檢查文件並返回該數據。 IPC(進程間通信)有許多選項 - 這不是一個非常漂亮的選項,但它具有最大可移植性的優點。

作爲一個方面說明 - 請不要傳遞字符串到setInterval(),傳遞函數。所以,你行應實際閱讀:

var progress = setInterval(ask, 500); 

但是 - 這將是更好的ask() AJAX功能的success/error處理程序使用setTimeout()。這是因爲通過使用setInterval(),無論前一個狀態如何,都會啓動一個新請求。在啓動下一個請求之前等待前一個請求結束會更有效率。所以我會做更像這樣的事情:

<script type="text/javascript"> 

    // We'll set this to true when the initail POST request is complete, so we 
    // can easily know when to stop polling the server for progress updates 
    var postComplete = false; 

    var ask = function() { 

    var time = new Date().getTime(); 

    $.ajax({ 

     type: 'get', 
     url: '/url/to/progress' + '?time=' + time, 

     success: function(data) { 
     $("#progress").html(data); 
     if (!postComplete) 
      setTimeout(ask, 500); 
     } 
     }, 
     error: function() { 
     // We need an error handler as well, to ensure another attempt gets scheduled 
     if (!postComplete) 
      setTimeout(ask, 500); 
     } 
     } 

    }); 

    } 

    $("#test").click(function() { 

    // Since you only ever call post() once, you don't need a seperate function. 
    // You can just put all the post() code here. 

    var time = new Date().getTime(); 

    $.ajax({ 

     type: 'post', 
     url: '/url/to/post' + '?time=' + time, 
     data: { 
     "some": "data" 
     }, 

     success: function(data) { 
     postComplete = true; 
     alert(data); 
     } 
     error: function() { 
     postComplete = true; 
     } 

    }); 

    if (!postComplete) 
     setTimeout(ask, 500); 
    } 

    }); 

</script> 

......雖然這仍然不能解決會話問題。

+0

您是可靠的。我發現函數session_write_close(),它似乎工作!我所需要做的只是在動作開始時關閉會話,並重新打開它僅用於更新會話變量。非常感謝,感謝您的建議。 – pawelo 2012-02-16 14:13:51

+0

小心在同一個腳本中多次打開和關閉會話數據 - 我看到有人抱怨說它不能可靠地工作,所以要確保你徹底測試了你所做的任何事情。 – DaveRandom 2012-02-16 14:15:18

+0

嗯,也許更好的解決方案是在Db或文件中存儲進度變量。我必須檢查出來,謝謝! – pawelo 2012-02-16 14:18:58

6

@DaveRandom上面正確地指出你是會話存儲鎖定的受害者。

解決方法非常簡單。您希望使處理post()的腳本釋放會話數據的鎖定,以便處理ask()的腳本可以訪問此會話數據。你可以用session_write_close來做到這一點。

小字這裏是調用session_write_close後,你將不能訪問會話變量,所以需要相應地結構中的腳本post

  1. 閱讀所有數據,您將需要從$_SESSION和保存它的副本。
  2. 撥打session_write_close釋放會話鎖定。
  3. 繼續您的冗長操作。如果您需要會話數據,請直接從您自己的副本中取出而不是$_SESSION

或者,您可以在腳本的生命週期內切換會議上多次鎖定:

session_start(); 
$_SESSION['name'] = 'Jon'; 

// Quick operation that requires session data 
echo 'Hello '.$_SESSION['name']; 

// Release session lock 
session_write_close(); 

// Long operation that does not require session data. 
sleep(10); 

// Need access to session again 
session_start(); 
echo 'Hello again '.$_SESSION['name']; 

這種佈置,這樣當它睡覺的腳本,其他腳本可以訪問會話數據,而不問題。

+0

我差點說「只是在'post'腳本中使用'session_write_close()',直到我重新讀這個問題,並意識到他正在嘗試通過會話數據共享漫長操作的進度。顯然,你不能這樣做。當您需要更新進度信息時,您可以嘗試'session_write_close()'和隨後的'session_start()' - 我從來沒有嘗試過這個過程,但我記得在這裏看到一個問題,有人發現這樣做沒有用,所以如果它能在任何地方工作,它可能無處不在。因此'你需要想出另一種分享數據的方式' – DaveRandom 2012-02-16 14:13:45