Estou fazendo alguns testes de unidade. A estrutura de teste carrega uma página em um iframe e, em seguida, executa asserções nessa página. Antes de cada teste começar, eu crio um Promise
que define o onload
evento do iframe para chamar resolve()
, define o do iframe src
e retorna a promessa.
Então, eu posso apenas ligar loadUrl(url).then(myFunc)
e ele vai esperar a página carregar antes de executar o que quer que myFunc
seja.
Eu uso esse tipo de padrão em todos os lugares em meus testes (não apenas para carregar URLs), principalmente para permitir que mudanças no DOM aconteçam (por exemplo, imitar clicar em um botão e esperar que divs sejam ocultados e exibidos).
A desvantagem desse design é que estou constantemente escrevendo funções anônimas com algumas linhas de código. Além disso, enquanto eu tenho uma solução alternativa (QUnit'sassert.async()
), a função de teste que define as promessas é concluída antes que a promessa seja executada.
Estou me perguntando se existe alguma maneira de obter um valor de a Promise
ou esperar (bloquear / suspender) até que seja resolvido, semelhante ao do .NET IAsyncResult.WaitHandle.WaitOne()
. Eu sei que o JavaScript é de thread único, mas espero que isso não signifique que uma função não possa produzir.
Em essência, existe uma maneira de fazer com que o seguinte mostre os resultados na ordem correta?
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>
fonte
.then(fnAppend.bind(myDiv))
, que podem reduzir enormemente os anons.Respostas:
A geração atual de Javascript nos navegadores não possui um
wait()
ousleep()
que permita que outras coisas sejam executadas. Então, você simplesmente não pode fazer o que está pedindo. Em vez disso, ele tem operações assíncronas que farão o seu trabalho e o chamarão quando terminar (como você vem usando promessas).Parte disso é devido ao único threadedness do Javascript. Se o thread único estiver girando, nenhum outro Javascript poderá ser executado até que o thread de giro seja concluído. ES6 apresenta
yield
e geradores que permitirão alguns truques cooperativos como esse, mas estamos longe de poder usá-los em uma grande variedade de navegadores instalados (eles podem ser usados em algum desenvolvimento do lado do servidor onde você controla o mecanismo JS que está sendo usado).O gerenciamento cuidadoso do código baseado em promessa pode controlar a ordem de execução de muitas operações assíncronas.
Não tenho certeza se entendi exatamente a ordem que você está tentando alcançar em seu código, mas você poderia fazer algo assim usando sua
kickOff()
função existente e, em seguida, anexar um.then()
manipulador a ela depois de chamá-la:Isso retornará a saída em uma ordem garantida - como esta:
Atualização em 2018 (três anos após a redação desta resposta):
Se você transpilar seu código ou executá-lo em um ambiente que ofereça suporte a recursos ES7 como
async
eawait
, agora pode usarawait
para fazer seu código "parecer" para aguardar o resultado de uma promessa. Ainda está se desenvolvendo com promessas. Ele ainda não bloqueia todo o Javascript, mas permite que você escreva operações sequenciais em uma sintaxe mais amigável.Em vez da maneira ES6 de fazer as coisas:
Você consegue fazer isso:
fonte
then()
é o que venho fazendo. Só não gosto de ter que escreverfunction() { ... }
o tempo todo. Isso confunde o código.(foo) => { ... }
em vez defunction(foo) { ... }
foo => single.expression(here)
para se livrar dos colchetes.async
eawait
sintaxe como uma opção que agora está disponível em node.js e em navegadores modernos ou via transpilers.Se estiver usando ES2016 você pode usar
async
eawait
e fazer algo como:Se estiver usando ES2015, você pode usar Geradores . Se você não gosta da sintaxe, pode abstraí-la usando uma
async
função de utilidade, conforme explicado aqui .Se estiver usando ES5, você provavelmente vai querer uma biblioteca como o Bluebird para ter mais controle.
Finalmente, se o seu tempo de execução suportar ES2015, a ordem de execução pode ser preservada com paralelismo usando Fetch Injection .
fonte
async
/await
.async
/await
é apenas outra sintaxe paraPromise.then
.async
não vai esperar ... a menos que ceda usandoawait
. O exemplo ES2016 / ES7 ainda não pode ser executado, então aqui está um código que escrevi três anos atrás, demonstrando o comportamento.Outra opção é usar Promise.all para esperar que uma série de promessas sejam resolvidas. O código a seguir mostra como esperar que todas as promessas sejam resolvidas e, em seguida, lidar com os resultados quando estiverem todas prontas (pois esse parecia ser o objetivo da pergunta); No entanto, start pode obviamente sair antes que o meio seja resolvido, apenas adicionando esse código antes de chamar resolve.
fonte
Você pode fazer isso manualmente. (Eu sei, essa não é uma boa solução, mas ..) use
while
loop atéresult
que não tenha um valorfonte