Я провожу модульное тестирование. Платформа тестирования загружает страницу в iFrame, а затем выполняет утверждения для этой страницы. Перед началом каждого теста я создаю объект, Promise
который устанавливает onload
событие iFrame для вызова resolve()
, устанавливает iFrame src
и возвращает обещание.
Итак, я могу просто позвонить loadUrl(url).then(myFunc)
, и он будет ждать загрузки страницы перед выполнением того, что myFunc
есть.
Я использую этот вид шаблонов повсюду в своих тестах (не только для загрузки URL-адресов), в первую очередь для того, чтобы позволить изменениям в DOM (например, имитировать нажатие кнопки и ждать, пока div скрываются и отображаются).
Обратной стороной этого дизайна является то, что я постоянно пишу анонимные функции с несколькими строками кода в них. Кроме того, хотя у меня есть обходной путь (QUnit's assert.async()
), тестовая функция, которая определяет обещания, завершается до того, как обещание будет выполнено.
Мне интересно, есть ли способ получить значение из a Promise
или подождать (блокировать / спать), пока он не разрешится, аналогично .NET IAsyncResult.WaitHandle.WaitOne()
. Я знаю, что JavaScript является однопоточным, но я надеюсь, что это не означает, что функция не может работать.
По сути, есть ли способ получить следующие результаты для вывода результатов в правильном порядке?
function kickOff() {
return new Promise(function(resolve, reject) {
$("#output").append("start");
setTimeout(function() {
resolve();
}, 1000);
}).then(function() {
$("#output").append(" middle");
return " end";
});
};
function getResultFrom(promise) {
// todo
return " end";
}
var promise = kickOff();
var result = getResultFrom(promise);
$("#output").append(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>
источник
.then(fnAppend.bind(myDiv))
, которые могут значительно сократить количество анонов.Ответы:
Текущее поколение Javascript в браузерах не имеет
wait()
илиsleep()
позволяет запускать другие вещи. Итак, вы просто не можете делать то, о чем просите. Вместо этого у него есть асинхронные операции, которые будут делать свое дело и затем вызывать вас, когда они будут выполнены (как вы использовали обещания для).Частично это связано с однопоточностью Javascript. Если один поток вращается, то никакой другой Javascript не может выполняться, пока этот вращающийся поток не будет завершен. В ES6 представлены
yield
генераторы, которые позволят использовать такие совместные приемы, но мы далеки от возможности использовать их в широком диапазоне установленных браузеров (их можно использовать в некоторых серверных разработках, где вы управляете движком JS. который используется).Тщательное управление кодом на основе обещаний может контролировать порядок выполнения многих асинхронных операций.
Я не уверен, что точно понимаю, какой порядок вы пытаетесь достичь в своем коде, но вы могли бы сделать что-то подобное, используя свою существующую
kickOff()
функцию, а затем прикрепив.then()
к ней обработчик после его вызова:Это вернет результат в гарантированном порядке - например:
Обновление в 2018 году (через три года после написания этого ответа):
Если вы либо транспилируете свой код, либо запускаете его в среде, которая поддерживает такие функции ES7, как
async
иawait
, теперь вы можете использовать,await
чтобы ваш код «появлялся» в ожидании результата обещания. Он все еще развивается с обещаниями. Он по-прежнему не блокирует весь Javascript, но позволяет писать последовательные операции в более удобном синтаксисе.Вместо способа работы ES6:
Ты можешь сделать это:
источник
then()
то, что делал. Мне просто не нравится писатьfunction() { ... }
все время. Это загромождает код.(foo) => { ... }
вместоfunction(foo) { ... }
foo => single.expression(here)
чтобы избавиться от фигурных и круглых скобок.async
иawait
синтаксис в качестве опции, которая теперь доступна в node.js и в современных браузерах или через транспилеры.Если вы используете ES2016, вы можете использовать
async
иawait
и делать что-то вроде:Если вы используете ES2015, вы можете использовать генераторы . Если вам не нравится синтаксис, вы можете абстрагироваться от него, используя
async
служебную функцию, как описано здесь. .Если вы используете ES5, вам, вероятно, понадобится библиотека, такая как Bluebird чтобы дать вам больше контроля.
Наконец, если ваша среда выполнения поддерживает ES2015, уже порядок выполнения может быть сохранен с помощью параллелизма с использованием Fetch Injection .
источник
async
/await
.async
/await
- это просто еще один синтаксис дляPromise.then
.async
не будет ждать ... если она не дает использованияawait
. Пример ES2016 / ES7 по-прежнему не может быть запущен. Итак, вот код, который я написал три года назад, демонстрирующий поведение.Другой вариант - использовать Promise.all для ожидания разрешения массива обещаний. В приведенном ниже коде показано, как дождаться выполнения всех обещаний, а затем обрабатывать результаты, когда все они будут готовы (поскольку это, по-видимому, и было целью вопроса); Тем не менее, очевидно, что start может выводиться до того, как middle будет разрешено, просто добавив этот код до того, как он вызовет resolve.
источник
Вы можете сделать это вручную. (Я знаю, что это не лучшее решение, но ...) используйте
while
цикл, покаresult
он не перестанет иметь значениеисточник
result
изменения, у результата никогда не будет возможности измениться, поскольку цикл событий никогда не сможет обработать что-либо еще. Иresult
это просто аргумент функции, который в любом случае никогда не меняется.