2015-06-07 25 views
6

Tôi đã xây dựng một trình thông dịch C trong C# một thời gian trước và bây giờ đã bắt đầu chuyển đổi nó sang Javascript. Mọi thứ đều ổn cho đến khi tôi nhận ra js không có chức năng ngủ. Trình thông dịch của tôi sử dụng một trình phân tích cú pháp đệ quy và nó tạm dừng cho đầu vào của người dùng trong khi nó được lồng vào một số hàm sâu (trong C# tôi đã sử dụng waithandle trong một luồng thứ hai). Tôi đã xem setInterval và setTimeout nhưng chúng không đồng bộ/không bị chặn; tất nhiên một busywait là ra khỏi câu hỏi và tôi nhìn vào một thực hiện timed_queue tôi tìm thấy trên SO nhưng không có may mắn. Tôi đã thử trình phân tích cú pháp cả trong cửa sổ chính và trong một webworker. Tôi đang sử dụng jQuery. Tôi có kinh nghiệm hạn chế với js và đang tìm kiếm ý tưởng để theo đuổi. Tôi biết rất ít về phong cách tiếp tục đi qua, hoặc năng suất và tôi tự hỏi nếu họ có thể giữ chìa khóa. Dưới đây là một chút cắt từ mã để hiển thị một số các controlscript. Bất kỳ ý tưởng xin vui lòng ...javascript vòng lặp lồng nhau chờ người dùng nhập

var STATE = { 
    START: "START", 
    RUN: "RUN", //take continuous steps at waitTime delay 
    STEP: "STEP", //take 1 step 
    PAUSE: "PAUSE",//wait for next step command 
    STOP: "STOP", 
    ERROR: "ERROR" 
} 
var state = state.STOP; 

function parsing_process() //long process we may want to pause or wait in 
{ 
    while(token !== end_of_file)// 
    { 
     //do lots of stuff - much of it recursive 
     //the call to getNextToken will be encountered a lot in the recursion 
     getNextToken(); 
     if (state === STATE.STOP) 
      break; 
    } 
} 

function getNextToken() 
{ 
    //retrieve next token from lexer array 
    if (token === end_of_line) 
    { 
     //tell the gui to highlight the current line 
     if (state === STATE.STOP) 
      return; 
     if (state === STATE.STEP)//wait for next step 
     { 
      //mimick wait for user input by using annoying alert 
      alert("click me to continue") 
     } 

     if (state === STATE.RUN) { 
      //a delay here - set by a slider in the window 
      //a busy wait haults processing of the window 
     } 
    } 
} 

Tôi đã nhận này để làm việc trong Firefox sử dụng task.js

<html> 
<head> 
    <title>task.js examples: sleep</title> 
    <script type="application/javascript" src="task.js"></script> 
</head> 
<body> 
    Only works in FIREFOX 
    <button onclick="step()">Step</button> 
    <button onclick="run()">Run</button> 
    <button onclick="stop()">Stop</button> 
    <pre style="border: solid 1px black; width: 300px; height: 200px;" id="out"> 
</pre> 

    <script type="application/javascript;version=1.8"> 

     function start() { 
      process(); 
     } 

     function step() { 
      if (state === STATE.STOP) 
       start(); 
      state = STATE.STEP; 
     } 
     function run() { 
      if (state === STATE.STOP) 
       start(); 
      state = STATE.RUN; 
     } 
     function stop() { 
      state = STATE.STOP; 
     } 

     var STATE = { 
      START: "START", 
      RUN: "RUN", //take continuous steps at sleepTime delay 
      STEP: "STEP", //take 1 step 
      PAUSE: "PAUSE",//wait for next step command 
      STOP: "STOP", 
      ERROR: "ERROR" 
     } 

     var state = STATE.STOP; 
     var sleepTime = 500; 

     function process() { 
      var { spawn, choose, sleep } = task; 
      var out = document.getElementById("out"); 
      var i=0; 
      out.innerHTML = "i="+i; 
      var sp = spawn(function() { 
       while(state !== STATE.STOP) 
       { 
        i++; 
        out.innerHTML = "i="+i; 
        if (state === STATE.RUN) 
        { 
         yield sleep(sleepTime); 
        } 
        if (state === STATE.STEP) 
         state = STATE.PAUSE; 
        while (state===STATE.PAUSE) 
        { 
         yield; 
        } 
       } 
      }); 
     } 
    </script> 
</body> 
</html> 

tôi sẽ đánh giá cao nếu ai đó biết điều gì đó về những lời hứa có thể cho tôi một số manh mối hơn. Ứng dụng của tôi không phải là một người tiêu dùng một nhưng nó sẽ được tốt đẹp nếu nó chạy trong hơn Firefox

+1

Bạn đang chạy môi trường gì? Sự vắng mặt của một chức năng 'ngủ 'chủ yếu là một điều * môi trường *, không phải là một thứ ngôn ngữ. –

+0

Bạn nên sử dụng lời hứa. – SLaks

+0

Bạn có thể giải thích cách sử dụng này, tức là, mục đích là gì? – Roberto

Trả lời

1

Là tác giả của JSCPP, tôi phải đối mặt với cùng một vấn đề chính xác khi tôi triển khai trình gỡ lỗi tạm dừng và tiếp tục phiên dịch chương trình khi đang di chuyển. Cuối cùng tôi quyết định sử dụng các chức năng của máy phát điện từ es6 nhưng tôi muốn chia sẻ quá trình suy nghĩ của tôi ở đây.

Cách thông thường là biên dịch mã mục tiêu đầu tiên thành mã byte không đệ quy cấp thấp. Bạn gắn nhãn mỗi câu lệnh rồi xử lý tất cả luồng điều khiển với unconditional jumpconditional jump. Sau đó, bạn chạy một thông dịch viên mã byte trên đó. Đây là một lựa chọn tốt nếu bạn không nhớ tất cả những công việc biên dịch này sẽ được thực hiện.

Cách khác là lưu lượng công việc "lưu trữ ngăn xếp cuộc gọi/tải ngăn xếp cuộc gọi". Khi bạn cần tạm dừng diễn giải, bạn đệ quy đẩy tất cả các đối số và tất cả các biến cục bộ vào một ngăn xếp tùy chỉnh theo cách ngược lại. Khi bạn cần tiếp tục thực hiện, bạn đệ quy tải tất cả các đối số và biến cục bộ này. Mã của bạn sẽ được biến đổi từ

AddExpression.prototype.visit = function(param) { 
    var leftVal = visit(this.left, param); 
    var rightVal = visit(this.right, param); 
    return leftVal + rightVal; 
} 

để

AddExpression.prototype.visit = function(param) { 
    if (needToStop) { 
     stack.push({ 
      method: AddExpression.prototype.visit, 
      _this: this, 
      params: [param], 
      locals: {}, 
      step: 0 
     }); 
     return; 
    } 
    if (recoverFromStop && stack.top().step === 0) { 
     var thisCall = stack.pop(); 
     if (stack.length > 0) { 
      var nextCall = stack.top(); 
      nextCall.method.apply(nextCall._this, params); 
     } 
    } 
    var leftvalue = visit(this.left, param); 
    if (needToStop) { 
     stack.push({ 
      method: AddExpression.prototype.visit, 
      _this: this, 
      params: [], 
      locals: { 
       leftvalue: leftvalue 
      }, 
      step: 1 
     }); 
     return; 
    } 
    if (recoverFromStop && stack.top().step === 1) { 
     var thisCall = stack.pop(); 
     leftvalue = thisCall.locals.leftvalue; 
     if (stack.length > 0) { 
      var nextCall = stack.top(); 
      nextCall.method.apply(nextCall._this, params); 
     } 
    } 
    var rightvalue = visit(this.right, param); 
    if (needToStop) { 
     stack.push({ 
      method: AddExpression.prototype.visit, 
      _this: this, 
      params: [], 
      locals: { 
       leftvalue: leftvalue, 
       rightvalue: rightvalue 
      }, 
      step: 2 
     }); 
     return; 
    } 
    if (recoverFromStop && stack.top().step === 2) { 
     var thisCall = stack.pop(); 
     leftvalue = thisCall.locals.leftvalue; 
     rightvalue = thisCall.locals.rightvalue; 
     if (stack.length > 0) { 
      var nextCall = stack.top(); 
      nextCall.method.apply(nextCall._this, params); 
     } 
    } 
    return leftvalue + rightvalue; 
}; 

Phương pháp này không làm thay đổi logic chính của thông dịch viên của bạn, nhưng bạn có thể xem cho chính mình như thế nào điên mã là dành cho một A + cú pháp B đơn giản .

Cuối cùng tôi quyết định sử dụng máy phát điện. Máy phát điện không có nghĩa là để thay đổi tương tác thực hiện chương trình, mà là cho mục đích đánh giá lười biếng. Nhưng với một số hack đơn giản, chúng ta có thể sử dụng đánh giá lười biếng báo cáo của chúng tôi khi nhận được lệnh "tiếp tục".

function interpret(mainNode, param) { 
    var step; 
    var gen = visit(mainNode); 
    do { 
     step = gen.next(); 
    } while(!step.done); 
    return step.value; 
} 

function visit*(node, param) { 
    return (yield* node.visit(param)); 
} 

AddExpression.prototype.visit = function*(param) { 
    var leftvalue = yield* visit(this.left, param); 
    var rightvalue = yield* visit(this.right, param); 
    return leftvalue + rightvalue; 
} 

Ở đây, function* nói rằng ta muốn các AddExpression.visit chức năng là chức năng máy phát điện. yield* theo sau là visit phương tiện gọi visit chức năng chính nó là một chức năng máy phát đệ quy.

Giải pháp này có vẻ hoàn hảo ngay từ cái nhìn đầu tiên nhưng nó đang bị giảm hiệu suất rất lớn bằng cách sử dụng máy phát (http://jsperf.com/generator-performance) và đó là từ es6 và không có nhiều trình duyệt hỗ trợ.

Để kết luận, bạn có ba cách khác nhau để đạt được thực hiện ngắt:

  1. biên dịch mã ở mức độ thấp:
    • Ưu điểm: thực tế phổ biến, tách biệt các mối quan tâm, dễ dàng để tối ưu hóa và duy trì
    • Nhược điểm: quá nhiều công việc
  2. lưu stack/tải stack:
    • Ưu điểm: tương đối nhanh, giữ lại giải thích lý
    • Nhược điểm: khó có thể duy trì
  3. máy phát điện:
    • Ưu điểm: dễ bảo trì, hoàn toàn giữ lại logic phiên dịch
    • Nhược điểm: chậm, cần es6 đến es5 transpiling
2

Nếu bạn đang chạy script của bạn trong trình duyệt và cần phải chờ đợi cho đầu vào của người dùng (nhấp vào sự kiện, lĩnh vực sự kiện thay đổi, vv) - sau đó bạn không thể sử dụng "while" và "pause" để đợi sự kiện của trình duyệt. Trình xử lý sự kiện sẽ được gọi không đồng bộ và vào thời điểm đó "vòng lặp" trong khi thậm chí có thể hoàn thành việc đọc danh sách các mã thông báo. Có lẽ bạn nên thử đọc mã thông báo bằng mã thông báo và dựa trên giá trị của nó - hãy gọi hành động tiếp theo.

Hãy thử ví dụ này: http://jsbin.com/puniquduqa/1/edit?js,console,output

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