2011-01-01 42 views
8

Có một hàm JavaScript, trong đó tôi không có quyền kiểm soát mã, gọi hàm mà tôi đã viết. Hàm của tôi sử dụng DOM để tạo một iFrame, định nghĩa nó là src và sau đó gắn nó vào một phần tử DOM khác. Tuy nhiên, trước khi hàm của tôi trả về, và do đó cho phép tiếp tục thực hiện hàm chứa, bắt buộc phải tải iFrame đầy đủ.Độ trễ/Javascript không thể nội tuyến không thể

Dưới đây là những điều mà tôi đã cố gắng và tại sao họ không làm việc:

1. Lựa chọn setTimeout:
99,999% thời gian, đây là câu trả lời. Thực tế, trong thập kỷ qua tôi đã cố vấn trong JavaScript, tôi đã luôn luôn nhấn mạnh rằng mã luôn có thể được tái cấu trúc để sử dụng tùy chọn này, và không bao giờ tin rằng một kịch bản tồn tại ở nơi không phải vậy. Vâng, cuối cùng tôi đã tìm thấy một! Vấn đề là bởi vì chức năng của tôi đang được gọi là inline, nếu dòng tiếp theo được thực hiện trước khi iFrame của tôi kết thúc tải, nó hoàn toàn trung lập kịch bản của tôi, và kể từ khi kịch bản của tôi hoàn thành, kịch bản bên ngoài tiếp tục. Gọi lại các loại sẽ không hoạt động

2. Vòng lặp "Không làm gì":
Tùy chọn này bạn sử dụng khi (// iFrame không được tải) {// do nothing}. Về lý thuyết, điều này sẽ không trở lại cho đến khi khung được nạp. Vấn đề là vì điều này hogs tất cả các nguồn lực, iFrame không bao giờ tải. Bí quyết này, mặc dù khủng khiếp không chuyên nghiệp, bẩn vv sẽ làm việc khi bạn chỉ cần một sự chậm trễ nội tuyến, nhưng kể từ khi tôi yêu cầu một chủ đề bên ngoài để hoàn thành, nó sẽ không.
Trong FF, sau một vài giây, nó tạm dừng kịch bản và một cảnh báo bật lên nói rằng có một kịch bản không phản hồi. Trong khi cảnh báo đó tăng, iFrame có thể tải, và sau đó chức năng của tôi có thể quay trở lại, nhưng có trình duyệt bị đóng băng trong 10 giây và sau đó yêu cầu người dùng loại bỏ một lỗi chính xác là không đi.

3. Mô hình đối thoại:
Tôi đã lấy cảm hứng từ một thực tế là FF cửa sổ bật lên cho phép iFrame để tải trong khi ngăn chặn việc thực hiện các chức năng, và suy nghĩ về nó, tôi nhận ra rằng đó là vì các phương thức đối thoại, là một cách tạm dừng thực hiện nhưng cho phép các chủ đề khác tiếp tục! Rực rỡ, vì vậy tôi quyết định thử các phương thức khác. Những thứ như alert() làm việc rất đẹp! Khi nó bật lên, ngay cả khi chỉ lên 1/10 giây, iFrame có thể hoàn thành và tất cả đều hoạt động tốt. Và chỉ trong trường hợp 1/10 giây không đủ, tôi có thể đặt đối thoại mô hình trong vòng lặp while từ giải pháp 2, và nó sẽ đảm bảo rằng iFrame được nạp đúng lúc. Ngọt phải không? Ngoại trừ một thực tế là bây giờ tôi phải bật lên một cuộc đối thoại rất không chuyên nghiệp cho người dùng để loại bỏ để chạy kịch bản của tôi. Tôi đã chiến đấu với bản thân mình về chi phí/lợi ích của hành động này, nhưng sau đó tôi gặp phải một tình huống mà mã của tôi được gọi là 10 lần trên một trang duy nhất! Phải loại bỏ 10 cảnh báo trước khi acessing một trang ?! Điều đó làm tôi nhớ đến những trang kiddie cuối thập niên 90 và không phải là một lựa chọn.

4. Một gazillion khác chậm trễ kịch bản trên mạng:
Có khoảng 10 jQuery chậm trễ hoặc ngủ chức năng, một số trong số họ thực sự khá khéo léo phát triển, nhưng không ai làm việc. Một vài lựa chọn nguyên mẫu, và một lần nữa, không có gì tôi tìm thấy có thể làm được! Hàng tá thư viện và khuôn khổ khác cho rằng họ có những gì tôi cần, nhưng tất cả họ đều âm mưu cho tôi hy vọng sai lầm.

Tôi tin rằng kể từ khi một cuộc đối thoại mô hình có thể dừng thực hiện, trong khi cho phép các chủ đề khác tiếp tục, phải có một số cách có thể truy cập mã để làm điều tương tự với đầu vào của người dùng.

Mã theo nghĩa đen là hàng ngàn hàng ngàn dòng và là độc quyền, vì vậy tôi đã viết ví dụ nhỏ về vấn đề này để bạn làm việc cùng.Điều quan trọng cần lưu ý các mã chỉ bạn có thể thay đổi là trong hàm onlyThingYouCanChange

Kiểm tra File:

<html> 
<head> 
</head> 
</html> 
<body> 
<div id='iFrameHolder'></div> 
<script type='text/javascript'> 
function unChangeableFunction() 
{ 
    new_iFrame = onlyThingYouCanChange(document.getElementById('iFrameHolder')); 
    new_iFrame_doc = (new_iFrame.contentWindow || new_iFrame.contentDocument); 
    if(new_iFrame_doc.document)new_iFrame_doc=new_iFrame_doc.document; 
    new_iFrame_body = new_iFrame_doc.body; 
    if(new_iFrame_body.innerHTML != 'Loaded?') 
    { 
     //The world explodes!!! 
     alert('you just blew up the world! Way to go!'); 
    } 
    else 
    { 
     alert('wow, you did it! Way to go!'); 
    } 
} 
var iFrameLoaded = false; 
function onlyThingYouCanChange(objectToAppendIFrameTo) 
{ 
    iFrameLoaded = false; 
    iframe=document.createElement('iframe'); 
    iframe.onload = new Function('iFrameLoaded = true'); 
    iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure that must be maintained. 
    objectToAppendIFrameTo.appendChild(iframe); 
    var it = 0; 
    while(!iFrameLoaded) //I put the limit on here so you don't 
    { 
     //If I was able to put some sort of delay here that paused the exicution of the script, but did not halt all other browser threads, and did not require user interaction we'd be golden! 
     //alert('test'); //This would work if it did not require user interaction! 
    } 
    return iframe; 
} 
unChangeableFunction(); 
</script> 
</body> 

blank_frame.html:

<html> 
<head> 
</head> 
<body style='margin:0px'>Loaded?</body> 
</html> 


ĐÂY LÀ ĐÁP tôi THỰC HIỆN TỪ CÁC LÝ TƯỞNG TỪ KHI ĐẾN TÁC GIẢ! BẠN GUYS ROCK!
nguồn mới của hàm tôi được phép thay đổi:

function onlyThingYouCanChange(objectToAppendIFrameTo) 
{ 
    iFrameLoaded = false; 
    iframe=document.createElement('iframe'); 
    iframe.onload = new Function('iFrameLoaded = true'); 
    iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure that must be maintained. 
    objectToAppendIFrameTo.appendChild(iframe); 
    var it = 0; 
    while(!iFrameLoaded) //I put the limit on here so you don't 
    { 
     if (window.XMLHttpRequest) 
     { 
      AJAX=new XMLHttpRequest(); 
     } 
     else 
     { 
      AJAX=new ActiveXObject("Microsoft.XMLHTTP"); 
     } 
     if (AJAX) 
     { 
      AJAX.open("GET", 'slow_page.php', false); 
      AJAX.send(null); 
     } 
     else 
     { 
      alert('something is wrong with AJAX!'); 
     } 

     //If I was able to put some sort of delay here that paused the exicution of the script, but did not halt all other browser threads, and did not require user interaction we'd be golden! 
     //alert('test'); //This would work if it did not require user interaction! 
    } 
    return iframe; 
} 

slow_page.php:

<? 
usleep(100000);//sleep for 1/10th of a second, to allow iFrame time to load without DOSing our own server! 
?> 

tôi muốn lưu ý rằng tôi nói rằng chẳng có gì ngoài chức năng đó mà tôi có thể thay đổi và thêm trang php đã vi phạm "quy tắc" đó nhưng trong trường hợp này tôi có thể làm điều đó. Nếu tôi không thể làm điều đó, tôi có thể đã được gọi là blank_frame.html thay vì slow_page.php và chỉ cần bao giờ cần gọi nó một lần (vì vậy 2 lần cho mỗi lần tải khung) giả định rằng nó phản hồi trong một lượng thời gian khi tải iFrame. Nếu vì lý do nào đó, tải iFrame chậm hơn, có thể gọi là 2ce (tổng số 3 cuộc gọi đến máy chủ)

+1

Bạn không thể kết hợp 'hàm mới() {return code; } 'với gọi lại của' setTimeout'? –

+2

Cá nhân tôi nghĩ rằng bất kỳ thiết kế nào yêu cầu trường hợp này là rất thiếu sót, nhưng từ góc độ học thuật, tôi muốn được quan tâm xem liệu có câu trả lời hay không. –

+0

meder, setTimeout sẽ cho phép thực thi Javascript liên tục, trừ khi bạn có cách kết hợp những thứ mà tôi không biết. Tôi đã đề cập đến một số plugin jQuery đã làm một số điều rất thông minh, với điều đó, nhưng tôi phân tích tất cả những gì tôi có thể tìm thấy và không có gì sẽ làm việc cho tôi. Nếu bạn có thể tìm cách để làm cho nó hoạt động trong ví dụ trên, tôi sẽ rất vui mừng! Brian. Tôi đồng ý với bạn 99,999%, và một vài ngày trước tôi đã nói 100%, và sẽ tranh cãi với bất cứ ai nói rằng sự trì hoãn nội tuyến là cần thiết. Thật không may tôi không có quyền kiểm soát để sửa nó ở đây! – trex005

Trả lời

1

NB Điều này vô cùng khó hiểu và tôi sẽ không sử dụng nó trong bất kỳ tình huống thực tế nào. Trong số các vấn đề tiềm năng khác, được cung cấp đủ lưu lượng truy cập, bạn có thể tự mình DDOS.

Bạn có thể tạo chức năng ngủ bằng cách thực hiện các cuộc gọi JAX không đồng bộ (A). Trong một số trình duyệt cũ hơn, điều này có thể đóng băng mọi thứ, nhưng ít nhất nó sẽ không yêu cầu bất kỳ loại phản hồi người dùng nào.

while (!iFrameLoaded) 
{ 
    if (XMLHTTPRequest) { 
     var request = new XMLHTTPRequest(); 
    } else { 
     var request = new ActiveXObject("Microsoft.XMLHTTP"); 
    } 

    request.open('GET', 'anyoldfile.htm', false); 
    request.send(); 

    // check if the iframe is loaded and set iFrameLoaded 
} 
+0

Tôi có thể đặt 100 url trên các máy chủ khác nhau nếu tôi cần (google, amazon, yahoo, whtismyip v.v.) cho các cuộc gọi ajax mà nó có thể lặp qua ... Tôi sẽ chỉ loại bỏ kết quả ... Tôi đi chơi với ý tưởng này một chút, tôi đoán là 1-2 cuộc gọi sẽ có nhiều sự chậm trễ. Tôi sẽ cho bạn biết làm thế nào nó đi! – trex005

+3

@ trex005 Với chính sách có nguồn gốc giống nhau, bạn sẽ cần URL trên tên miền của mình, nếu không yêu cầu sẽ không thành công ngay lập tức. – lonesomeday

2

Điều bạn thực sự cần là một sự kiện được kích hoạt khi nội dung iFrame đã tải. Điều này thực sự thực sự dễ dàng vì trang bên trong iFrame có các sự kiện riêng và nó có thể truy cập các tập lệnh trên trang gốc. Bạn sẽ cần phải có thể thay đổi nội dung của iFrame.

Trong iFrame của bạn, bạn sẽ cần phải đoạn mã này

// Use whichever DOMReady function you like, or window.onload would work 
window.addEventListener('DOMContentLoaded', function() { 
    if (parent.window.myFunction) { 
     parent.window.myFunction(); 
    } 
}, false);

Sau đó, trong trang cha mẹ của bạn, làm cho một chức năng gọi là "myFunction" và đặt tất cả các kịch bản bạn cần kích hoạt để ở đó. Điều này sẽ làm việc mọi lúc.

Chỉnh sửa: Để thực hiện việc này, bạn thực sự cần hai chức năng. Tôi giả định rằng đó thực sự không phải là một lựa chọn vì vậy chúng tôi sẽ hack một chức năng để chứa hai chức năng và gọi đúng phần khi chúng ta cần nó.

 
function onlyThingYouCanChange(stringOrObject) { 
    function createIFrame(objectToAppendIFrameTo) { 
     // This comment represents all the code that appends your iFrame 
    } 
    function onIFrameReady() { 
     // This comment represents all the stuff you want to happen when the iFrame is ready 
    } 

    // The bones of it 
    if (stringOrObject === "iFrameLoaded") { 
     onIFrameReady(); 
    } else { 
     createIFrame(stringOrObject); 
    } 
} 

Các kịch bản trong iFrame bây giờ sẽ được thay đổi một cái gì đó như thế này:

// Use whichever DOMReady function you like, or window.onload would work 
window.addEventListener('DOMContentLoaded', function() { 
    if (parent.window.onlyThingYouCanChange) { 
     parent.window.onlyThingYouCanChange('iFrameLoaded'); 
    } 
}, false);

tôi đã không kiểm tra nó, nhưng về mặt lý thuyết rằng nên làm điều đó

+0

Có một số vấn đề với điều này trong đó tôi đã đề cập đến câu hỏi của mình. 1) Tôi không thể thay đổi bất cứ điều gì ngoài chức năng mà tôi đã chỉ định. Lý do đằng sau điều này rất phức tạp, vì vậy tôi chỉ phải tin tôi vào cái này 2) Nếu bạn nhìn vào mã của tôi, bạn sẽ thực sự thấy tôi đã gọi một hàm khi iFrame kết thúc tải. Đó là phần đơn giản. Vấn đề là, tôi cần phải trì hoãn phần còn lại của việc thực thi mã tiếp theo cho đến khi sự kiện đó được kích hoạt, mà tôi không thấy giải pháp của bạn làm như thế nào cả. – trex005

+0

Nếu có bất kỳ cách nào bạn có thể nhận được giải pháp của bạn để làm việc với mẫu của tôi, ngay cả khi bạn phải thay đổi blank_frame.html, tôi cũng có thể hoàn thành nó theo cách sáng tạo. – trex005

+0

OK, tôi đã chỉnh sửa bài đăng này. Điều đó có giúp ích gì không? –

0

Tại sao bạn không sửa đổi mã cơ sở? Ví dụ, nó có thể là khá đơn giản để thay đổi chức năng cốt lõi từ

function unChangeableFunction() 
{ 
    new_iFrame = onlyThingYouCanChange(document.getElementById('iFrameHolder')); 
    new_iFrame_doc = (new_iFrame.contentWindow || new_iFrame.contentDocument); 
    if(new_iFrame_doc.document)new_iFrame_doc=new_iFrame_doc.document; 
    new_iFrame_body = new_iFrame_doc.body; 
    if(new_iFrame_body.innerHTML != 'Loaded?') 
    { 
     //The world explodes!!! 
     alert('you just blew up the world! Way to go!'); 
    } 
    else 
    { 
     alert('wow, you did it! Way to go!'); 
    } 
} 

Để một cái gì đó như thế này:

function unChangeableFunction() 
{ 
    var new_iFrame = onlyThingYouCanChange(document.getElementById('iFrameHolder')); 
    new_iFrame.onload = function() 
    { 
     new_iFrame_doc = (new_iFrame.contentWindow || new_iFrame.contentDocument); 
     if(new_iFrame_doc.document)new_iFrame_doc=new_iFrame_doc.document; 
     new_iFrame_body = new_iFrame_doc.body; 
     if(new_iFrame_body.innerHTML != 'Loaded?') 
     { 
      //The world explodes!!! 
      alert('you just blew up the world! Way to go!'); 
     } 
     else 
     { 
      alert('wow, you did it! Way to go!'); 
     } 
    }; 
} 

Nếu điều đó không làm việc cho bạn, làm thế nào về một sửa đổi trong suốt của mã gốc? Biên dịch với Javascript Strands và sử dụng hỗ trợ tương lai tích hợp để xử lý việc này. Lưu ý rằng Javascript 1.7 cũng hỗ trợ tính năng tiếp tục, nhưng sẽ yêu cầu thay đổi mã theo cách thủ công để sử dụng chúng.

3

Vâng, thực tế là javascript là chuỗi đơn thực sự cắn bạn ở đây. Bạn có thể sử dụng một cuộc gọi ajax đồng bộ đến một trang có mục đích chậm để mô phỏng một giấc ngủ, nhưng bạn sẽ không nhận được kết quả mong muốn. Tại sao bạn không chỉ đảm bảo rằng IFrame của bạn được tải trước khi chức năng không thay đổi được gọi?

+0

chức năng không thể thay đổi gọi hàm duy nhất mà tôi có quyền kiểm soát, vì vậy tôi sẽ không có bất kỳ cách nào để giới thiệu một sự chờ đợi trước đó. Tôi đang khám phá ajax đồng bộ mặc dù. – trex005

+0

Vâng, nếu bạn làm điều đó, bạn không cần phải nhấn trang hàng trăm lần, tất cả những gì bạn cần làm là đặt một giấc ngủ trong php/jsp của bạn, hoặc bất cứ điều gì khác bạn sử dụng. Thêm một giấc ngủ 100ms, và tải nó 10 lần sẽ cung cấp cho bạn một giấc ngủ 1 giây. – Zeki

+0

Bạn đã cho tôi một chìa khóa rất quan trọng trong việc giải quyết vấn đề này (kịch bản PHP ngủ), nhưng tôi chỉ có thể chấp nhận một câu trả lời. Tôi ước tôi có thể chấp nhận cả hai. Cảm ơn bạn! – trex005

0

Một giải pháp khác có thể không áp dụng được, tùy thuộc vào số tiền bạn đã đơn giản hóa mã gốc. Bạn có thể thiết lập một handler onload, sau đó ném ra một lỗi, sau đó gọi unChangeableFunction trong handler onload của bạn:

function onlyThingYouCanChange(objectToAppendIFrameTo) 
{ 
    // using global variable func_called 
    if (!func_called) { 
     func_called = true; 
     var iframe=document.createElement('iframe'); 
     iframe.src = 'blank_frame.html'; 
     iframe.id = 'myIframe'; 
     iframe.onload = function() { 
      unChangeableFunction(); 
     }; 
     objectToAppendIFrameTo.appendChild(iframe); 

     throw new Error('not an error'); 
    } else { 
     return document.getElementById('myIframe'); 
    } 
} 

Chức năng này (như unChangeableFunction) sẽ được gọi hai lần: một lần trong trường hợp đầu tiên, sau đó một lần nữa khi xử lý onload được kích hoạt. Hai con đường khác nhau phản ánh điều này.

Một lần nữa, điều này là hacky và lạm dụng nhất định chức năng lỗi của JS.

0

bạn có thể sử dụng cookie và setTimeout như thế:

trong blank_frame.html thêm một kịch bản:

<script type="text/javascript"> 
function deleteCookie(cookie_name) 
{ 
    var cookie_date=new Date(); 
    cookie_date.setTime(cookie_date.getTime()-1); 
    document.cookie=cookie_name+="=;expires="+cookie_date.toGMTString(); 
} 
function setCookie(name,value,expires,path,domain,secure){ 
    document.cookie=name+"="+escape(value)+((expires)?"; expires="+expires.toGMTString():"")+((path)?"; path="+path:"")+((domain)?"; domain="+domain:"")+((secure)?"; secure":""); 
} 

window.onload=function(){ 
    setCookie('iframe_loaded','yes',false,'/',false,false); 
} 
</script> 

Về cơ bản bạn đang thêm một cookie iframe_loaded với giá trị yes. IMO tốt hơn nên xóa cookie khi bạn cần làm như vậy nếu bạn tải lại trang. Bạn cũng có thể đặt tên miền trong lệnh gọi hàm setCookie.

Bây giờ trong tập tin chính chúng ta sẽ sử dụng setTimeout với chức năng sẽ kiểm tra xem cookie tồn tại, nếu có thì hàm sẽ trả về iframe như trong mã của bạn:

function onlyThingYouCanChange(objectToAppendIFrameTo) 
{ 
    function get_cookie(cookie_name){ 
     var results = document.cookie.match('(^|;) ?'+cookie_name+'=([^;]*)(;|$)'); 
     return results?unescape(results[2]):null; 
    } 
    function deleteCookie(cookie_name){ 
     var cookie_date=new Date(); 
     cookie_date.setTime(cookie_date.getTime()-1); 
     document.cookie=cookie_name+="=;expires="+cookie_date.toGMTString(); 
    } 

    iFrameLoaded = false; 
    iframe=document.createElement('iframe'); 
    iframe.onload = new Function('iFrameLoaded = true'); 
    iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure that must be maintained. 
    objectToAppendIFrameTo.appendChild(iframe); 
    var it = 0; 

    function checkiframe(){ 
     if(get_cookie('iframe_loaded')=="yes"){ 
      alert('iframe loaded'); 
      deleteCookie('iframe_loaded'); 
      return iframe; 
     }else{ 
      setTimeout(checkiframe,1000); 
     } 
    } 

    checkiframe(); 
} 

Như một cookie failsafe đang được cũng đã xóa trong tệp này. Hy vọng rằng sẽ cung cấp cho bạn một cái gì đó để làm việc với :)

Cheers

G.

1

Một stupefyingly đơn giản; -} câu trả lời bằng XPCOM:

// Get instance of the XPCOM thread manager. 
var threadManager=Components.classes['@mozilla.org/thread-manager;1'].getService(
         Components.interfaces.nsIThreadManager); 
// Release current thread. 
function doThread() {threadManager.currentThread.processNextEvent(false);}; 

// Event enabled delay, time in ms. 
function delay(time) { 
    var end; 
    var start=Date.now(); 
    do { 
    end=Date.now(); 
    doThread(); 
    } while ((end-start) <= time); 
} 

trình trong phiên bản mới của Firefox . Xin lỗi không có hy vọng cho Explorer!

1

Chức năng đệ quy có thể trợ giúp trong trường hợp này. chỉ cần gọi hàm cho đến khi biến toàn cầu chỉ ra rằng khung được tải

var iFrameStarted = false; //you need two global vars 
var iFrameLoaded = false; 


function onlyThingYouCanChange(objectToAppendIFrameTo) 
{ 
    if (iFrameLoaded=false) // if the frame has loaded then you are done. skip everything and return iframe 
    { if (iFrameStarted = false) //otherwise start the frame if it has not been 
    { 
    iFrameStarted = true; 
    iframe=document.createElement('iframe'); 
    iframe.onload = new Function('iFrameLoaded = true'); 
    iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure 
    objectToAppendIFrameTo.appendChild(iframe); 
    var it = 0; 
    for (i=0;i<10000;i++) {} //slow down execution so you are not recursing yourself to death 
    onlyThingYouCanChange(objectToAppendIFrameTo); //start the recursion process 

    } 
    else //the frame has been started so continue recursion until the frame loaded 
    { 
    for (i=0;i<10000;i++) {} //slow down execution so you are not recursing yourself to death 
    onlyThingYouCanChange(objectToAppendIFrameTo); recursively call your function until the frame is loaded 

    } 

} 

return iframe; //you only get here when all the recursions are finished 
} 
+0

Liếc qua điều này, tôi không thấy nó khác với '2. Vòng lặp "Không làm gì" Bạn có thể xây dựng? – trex005

Các vấn đề liên quan