2016-05-07 6 views
3

Я пытаюсь склонить голову к обещаниям и тому, как работает JavaScript с его очередью и циклом событий и т. Д.Почему синхронная функция сна не выполнена async, будучи внутренне обещанием?

Я думал, что если я поставлю медленную синхронную функцию внутри обещания, эта медленная функция синхронизации будет делегирована на задний план, и я мог бы использовать .then, чтобы справиться с ним, когда это было сделано.

function syncSleep(ms){ 
    var end = new Date().getTime() + ms; 
    var start = new Date().getTime(); 

    while (start < end) { 
     start = new Date().getTime(); 
    } 
} 

function p() { 
    return new Promise(function(resolve) { 
    syncSleep(5000); 
    resolve("syncSleep done!"); 
    }); 
} 

p().then(function(s) { 
    var div = document.getElementById('async'); 
    div.innerHTML = s; 
}); 

var div = document.getElementById('sync'); 
div.innerHTML = "This should appear right away! (but it doesn't)"; 

https://jsfiddle.net/7mw6m2x5/

Пользовательский интерфейс не отвечает на запросы в то время как этот код работает, хотя.

Так что мне было интересно, может кто-нибудь объяснить, что здесь происходит? Обещают только способ обработки кода, который уже «сделан быть» async?

(Если да, то как это делается?)

Как я справляюсь с медленным кодом синхронизации, если я не хочу, чтобы заморозить UI? Должен ли я использовать веб-исполнителя для этого?

Благодарен за любые разъяснения. Благодарю.

+0

_ ", и я мог бы использовать. То, чтобы справиться с ним, когда это было сделано" _ Нет значения, похоже, возвращается из 'syncSleep'? _ «Как мне работать с медленным кодом синхронизации, когда я не хочу, чтобы он замораживал пользовательский интерфейс?» _ В чем заключается фактический «javascript»? Чего вы пытаетесь достичь? – guest271314

+0

@ guest271314, я просто пытаюсь понять, как работает этот JavaScript. У меня была гипотеза о том, как этот код будет работать, но оказалось, что я ошибался. Итак, теперь я пытаюсь исправить свое понимание :-) –

+0

Да, для этого вам следует использовать WebWorkers или child_process/cluster. Невозможно обрабатывать медленный код синхронизации на одном потоковом языке. В качестве альтернативы, если вам разрешено изменять медленный код, вы можете переписать его с помощью генераторов/итераторов ES6 или как они были вызваны. – Dodekeract

ответ

4

Код работает, как ожидалось.

Поскольку Javascript является одиночным, пользовательский интерфейс будет блокироваться во время выполнения вашего цикла.

Обещания - это просто хороший способ обработки асинхронного кода. См введение здесь:

http://www.html5rocks.com/en/tutorials/es6/promises/

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Чтобы быть в состоянии поддерживать reponsive UI, а другой код выполняется в фоновом режиме, вам нужно будет использовать WebWorkers:

https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers

Процитировать с указанной выше страницы:

«Веб-рабочие предоставляют простые средства для веб-контента для запуска сценариев в фоновом потоке. Рабочий поток может выполнять задачи без вмешательства в пользовательский интерфейс «

Update:.

следующие комментарии, я вписываться этот небольшой скрипт, чтобы продемонстрировать разницу между блокирование и не блокирование подходов Существует. некоторые повторы в коде, но я думаю, что это достаточно просто понять.

function setVal(s) { 
 
    var divAsync = document.getElementById('async'); 
 
    var innerDiv = document.createElement('div'); 
 
    innerDiv.innerHTML = s + '<br>'; 
 
    divAsync.appendChild(innerDiv); 
 
} 
 

 

 
function syncSleep(ms) { 
 
    var end = new Date().getTime() + ms; 
 
    var now = new Date().getTime(); 
 
    var stepBegin = new Date().getTime(); 
 
    var step = 0; 
 

 
    // This loop is blocking 
 
    // The UI will only refresh after the loop completion 
 
    while (now < end) { 
 
    now = new Date().getTime(); 
 
    step = now - stepBegin; 
 
    if (step >= 1000) { 
 
     setVal(now); 
 
     step = 0; 
 
     stepBegin = now; 
 
    } 
 
    } 
 

 
} 
 

 
function pBlock() { 
 
    return new Promise(function(resolve) { 
 
    syncSleep(3200); 
 
    resolve("pBlock syncSleep done!"); 
 
    }); 
 
} 
 

 

 
function noBlockUpdate(ms, resolve) { 
 
    var end = new Date().getTime() + ms; 
 
    var now = new Date().getTime(); 
 
    var stepBegin = new Date().getTime(); 
 
    var step = 0; 
 

 
    function noBlock() { 
 
    now = new Date().getTime(); 
 

 
    // paint every 1000ms; 
 
    step = now - stepBegin; 
 
    if (step >= 1000) { 
 
     setVal(now); 
 
     step = 0; 
 
     stepBegin = now; 
 
    } 
 

 
    if (now < end) { 
 
     // NB: this is going to be called thousands of times 
 
     // But the UI will still update every 1000 ms 
 
     setTimeout(noBlock); 
 
    } else { 
 
     resolve("pNoBlock done!"); 
 
    } 
 
    }; 
 
    noBlock(); 
 
} 
 

 
function pNoBlock() { 
 
    return new Promise(function(resolve) { 
 
    noBlockUpdate(3200, resolve); 
 
    setVal("pNoBlock launched!"); 
 
    }); 
 
} 
 

 

 

 
pNoBlock().then(setVal); 
 

 
var divSync = document.getElementById('sync'); 
 
divSync.innerHTML = "This appears right away!"; 
 

 

 

 
// Just wait 4 seconds so the non-blocking code completes 
 
setTimeout(function() { 
 
    // Clear all div's 
 
    document.getElementById('sync').innerHTML = ''; 
 
    document.getElementById('async').innerHTML = ''; 
 

 
    var divSync = document.getElementById('sync'); 
 
    divSync.innerHTML = "This does not appear right away, only after the blocking operation is complete!"; 
 

 
    pBlock().then(setVal); 
 
}, 4000);
<!DOCTYPE html> 
 
<html> 
 

 
<head> 
 
    <meta charset="utf-8"> 
 
    <meta name="viewport" content="width=device-width"> 
 
    <title>JS Bin</title> 
 
</head> 
 

 
<body> 
 

 
    <div id="sync"></div> 
 

 
    <div id="async"></div> 
 

 
</body> 
 

 
</html>

+1

Но почему же код внутри Promise не переместился в конец очереди? То есть почему код сразу же замораживается, а не после «div.innerHTML = ...»(внизу) –

+0

Страница JS имеет только один доступный поток (если вам нужна работа вне нитей, вам нужно использовать веб-мастеров или более общего работника службы). Ваше обещание может и, следовательно, немедленно выполнить, и поэтому немедленно блокирует поток, запустив его (невероятно плохо, потому что он заставит CPU перегружать - пять секунд 100% -ного процессора ...). Цикл вращения –

+0

@ Mike'Pomax'Kamermans Если я использую «* setTimeout (() => {syncSleep (5000); разрешить («syncSleep done !!»)}, 0) * «внутри Promise, он ведет себя так, как я ожидал. Правильно ли я считаю, что это потому, что * setTimeout() * обеспечивается DOM, и он фактически запускает * syncSleep * в другом потоке –

2

Обещания не являются нитями. Это всего лишь сахар для обработки успешных и неудачных событий (обратных вызовов) в однопоточном коде.

Обратный звонок для new Promise(cb) Конструктор выполняется немедленно и синхронно.Обратные вызовы, выданные .then(cb)/.catch(cb), выполняются на следующем тике после того, как обещание будет разрешено/отклонено, но они также работают на одной и той же стороне.

3

Есть ли обеим сторонам только способ обработки кода, который уже «сделал быть асинхронным»?

Да. Обещания действительно не создают асинхронные операции. Их целью является просто упростить работу с асинхронными операциями, определив согласованный API. Но они не избегают блокировки самостоятельно за одну небольшую задержку между моментами, когда вызываются обратные вызовы resolve() и .then().

Функция, предоставляемая конструктору Promise, вызывается немедленно и синхронно. И, как только начнется обратный вызов .then(), он должен будет работать до завершения, прежде чем движок сделает что-нибудь еще.

Как мне работать с медленным кодом синхронизации, когда я не хочу его замораживать?

Старайтесь избегать длительных синхронных операций в максимально возможной степени. Асинхронная альтернатива syncSleep() будет setTimeout().

function p() { 
    return new Promise(function(resolve) { 
    setTimeout(function() { 
     resolve("syncSleep done!"); 
    }, 5000); 
    }); 
} 

Если затянувшийся синхронной работы нельзя избежать, то вы хотите, чтобы попытаться переместить его в отдельный процесс/экземпляра двигателя - и, в браузерах, которые могут быть сделаны с Веб-работники.

Возможно, существует возможное промежуточное заземление, если вы можете разбить операцию на несколько этапов, чтобы отделить каждый шаг кратким setTimeout(). Эти перерывы периодически дают время работы двигателя для других событий, включая обновление страницы для пользователя. Вы хотите, чтобы каждый шаг был небольшим, чтобы иметь как можно больше разрывов, так как каждый шаг все равно блокирует все остальное после его запуска.

+0

Спасибо за ваш ответ, конечно, я бы никогда не использовал синхронную функцию сна в реальном приложении. Я сделал это по педагогическим соображениям; пытаясь понять, как это работает. –