Как дождаться обещания JavaScript для разрешения перед возобновлением функции?

Я провожу модульное тестирование. Тестовая платформа загружает страницу в iFrame, а затем запускает утверждения на этой странице. Перед началом каждого теста я создаю Promise который устанавливает iFrame в onload событие, чтобы вызвать resolve(), устанавливает iFrame в src, и возвращает обещание.

Итак, я могу просто позвонить loadUrl(url).then(myFunc), и он будет ждать загрузки страницы, перед выполнением любой myFunc есть.

Я использую такой шаблон повсюду в своих тестах (не просто для Загрузки URL-адресов), в первую очередь для того, чтобы позволить изменениям в DOM произойти (например, mimick, нажав кнопку, и ждать divs, чтобы скрыть и показать).

недостатком этого дизайна является то, что я постоянно пишу анонимные функции с несколькими строками кода в них. Кроме того, хотя у меня есть работа-вокруг (помощью QUnit по 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>

1 ответов


мне интересно, есть ли способ получить ценность от обещания или подождите (блок / сон), пока он не разрешится, подобно .NET Объекта iasyncresult.Объекта waithandle.WaitOne(). Я знаю, что JavaScript однопоточный, но я надеюсь, что это не означает, что функция не могу уступить.

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

отчасти это потому, что один threadedness в JavaScript. Если один поток вращается, то никакой другой Javascript не может выполняться, пока этот вращающийся поток не будет выполнен. ЕС6 вводит yield и генераторы, которые позволят некоторые совместные трюки, как это, но мы довольно далеко от возможности использовать их в широком диапазоне установленных браузеров (они может использоваться в какой-либо серверной разработке, где вы управляете используемым движком JS).


тщательное управление кодом на основе обещаний может контролировать порядок выполнения группы асинхронных операций.

Я не уверен, что понимаю, какой именно порядок вы пытаетесь достичь в своем коде, но вы можете сделать что-то подобное, используя свой существующий kickoff() функция, а затем просто прикрепление .then() обработчик к нему после вызова это:

function kickOff() {
  return new Promise(function(resolve, reject) {
    $("#output").append("start");

    setTimeout(function() {
      resolve();
    }, 1000);
  }).then(function() {
    $("#output").append(" middle");
    return " end";
  });
}

kickoff().then(function(result) {
    // use the result here
    $("#output").append(result);
});

это даст вам выход, как это в гарантированном порядке:

start
middle
end

обновление в 2018 году (через три года после написания этого ответа):

если вы либо транспилировать код или запустить код в среде, которая поддерживает функции ES7, такие как async и await, теперь вы можете использовать await чтобы ваш код "появился", чтобы дождаться результата обещания. Он все еще развивается с обещаниями. Это все еще не так. заблокируйте весь Javascript, но он позволяет писать последовательные операции в более удобном синтаксисе.

вместо способа ES6 делать вещи:

someFunc().then(someFunc2).then(result => {
    // process result here
}).catch(err => {
    // process error here
});

вы можете сделать это:

// returns a promise
async function wrapperFunc() {
    try {
        let r1 = await someFunc();
        let r2 = await someFunc2(r1);
        // now process r2
        return someValue;     // this will be resolved value of the returned promise
    } catch(e) {
        console.log(e);
        throw e;      // let caller know the promise rejected with this reason
    }
}

wrapperFunc().then(result => {
    // got final result
}).catch(err => {
    // got error
});