如何在恢复功能之前等待JavaScript Promise解决? [英] How to wait for a JavaScript Promise to resolve before resuming function?

查看:93
本文介绍了如何在恢复功能之前等待JavaScript Promise解决?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在做一些单元测试。测试框架将页面加载到iFrame中,然后针对该页面运行断言。在每次测试开始之前,我创建一个 Promise ,它设置iFrame的 onload 事件来调用 resolve (),设置iFrame的 src ,并返回承诺。



所以,我可以调用 loadUrl(url).then(myFunc),它会在执行任何 myFunc 是。



我在我的测试中使用这种模式(不只是用于加载URL),主要是为了允许更改DOM发生(例如模仿点击一个按钮,然后等待div隐藏和显示)。



这个设计的缺点是我经常写一个匿名函数其中几行代码。此外,虽然我有一个解决方法(QUnit的 assert.async()),定义promise的测试函数在promise运行之前就完成了。



我想知道是否有办法从 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 );返回end;});}; function getResultFrom(promise){// todo returnend;} 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>  

解决方案


我想知道是否有办法获得来自Promise的值或
等待(阻塞/休眠)直到它已解决,类似于.NET的
IAsyncResult.WaitHandle.WaitOne()。我知道JavaScript是
单线程,但我希望这并不意味着
函数无法产生。


浏览器中当前一代的Javascript没有 wait() sleep()允许其他事情运行。所以,你根本无法做你所要求的。相反,它有异步操作,可以完成他们的事情,然后在他们完成时打电话给你(因为你一直在使用promises)。



部分是因为Javascript的单线程。如果单个线程正在旋转,那么在旋转线程完成之前,其他任何Javascript都无法执行。 ES6引入了 yield 和生成器,这将允许这样的一些合作技巧,但我们还有很多方法可以在大量安装的浏览器中使用它们(它们可以用于控制正在使用的JS引擎的服务器端开发。






认真管理承诺基于代码的代码可以控制许多异步操作的执行顺序。



我不确定我是否完全理解你在代码中尝试实现的顺序,但是您可以使用现有的 kickOff()函数执行此类操作,然后将 .then()处理程序附加到它之后调用它:

  function kickOff(){
返回新的Promise(函数(解析,拒绝){
$(#output)。append(start);

setTimeout(function(){
resolve();
},1000);
})。then(function(){
$(#output)。append(middle);
返回结束;
});
}

kickOff()。then(function(result){
//在这里使用结果
$(#output)。append(result) ;
});

这将以保证的顺序返回输出 - 如下所示:

 开始
中间
结束

2018年更新(写完这个答案后三年):



如果要么转换代码或运行代码在支持ES7功能的环境中,例如 async await ,您现在可以使用等待使您的代码显示以等待承诺的结果。它仍在以承诺发展。它仍然不会阻止所有Javascript,但它确实允许您以更友好的语法编写顺序操作。



而不是ES6的处理方式:

  someFunc()。then(someFunc2).then(result => {
//处理结果在这里
})。catch(err => {
//这里处理错误
});

你可以这样做:

  //返回一个promise 
async function wrapperFunc(){
try {
let r1 = await someFunc();
让r2 = await someFunc2(r1);
//现在处理r2
返回someValue; //这将是返回的promise的解析值
} catch(e){
console.log(e);
throw e; //让来电者知道承诺被拒绝了这个原因
}
}

wrapperFunc()。then(result => {
//得到最终结果
})。catch(错误=> {
//得到错误
});


I'm doing some unit testing. The test framework loads a page into an iFrame and then runs assertions against that page. Before each test begins, I create a Promise which sets the iFrame's onload event to call resolve(), sets the iFrame's src, and returns the promise.

So, I can just call loadUrl(url).then(myFunc), and it will wait for the page to load before executing whatever myFunc is.

I use this sort of pattern all over the place in my tests (not just for loading URLs), primarily in order to allow changes to the DOM to happen (e.g. mimick clicking a button, and wait for divs to hide and show).

The downside to this design is that I'm constantly writing anonymous functions with a few lines of code in them. Further, while I have a work-around (QUnit's assert.async()), the test function that defines the promises completes before the promise is run.

I'm wondering if there is any way to get a value from a Promise or wait (block/sleep) until it has resolved, similar to .NET's IAsyncResult.WaitHandle.WaitOne(). I know JavaScript is single-threaded, but I'm hoping that doesn't mean that a function can't yield.

In essence, is there a way to get the following to spit out results in the correct order?

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>

解决方案

I'm wondering if there is any way to get a value from a Promise or wait (block/sleep) until it has resolved, similar to .NET's IAsyncResult.WaitHandle.WaitOne(). I know JavaScript is single-threaded, but I'm hoping that doesn't mean that a function can't yield.

The current generation of Javascript in browsers does not have a wait() or sleep() that allows other things to run. So, you simply can't do what you're asking. Instead, it has async operations that will do their thing and then call you when they're done (as you've been using promises for).

Part of this is because of Javascript's single threadedness. If the single thread is spinning, then no other Javascript can execute until that spinning thread is done. ES6 introduces yield and generators which will allow some cooperative tricks like that, but we're quite a ways from being able to use those in a wide swatch of installed browsers (they can be used in some server-side development where you control the JS engine that is being used).


Careful management of promise-based code can control the order of execution for many async operations.

I'm not sure I understand exactly what order you're trying to achieve in your code, but you could do something like this using your existing kickOff() function, and then attaching a .then() handler to it after calling it:

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);
});

This will return output in a guaranteed order - like this:

start
middle
end

Update in 2018 (three years after this answer was written):

If you either transpile your code or run your code in an environment that supports ES7 features such as async and await, you can now use await to make your code "appear" to wait for the result of a promise. It is still developing with promises. It does still not block all of Javascript, but it does allow you to write sequential operations in a friendlier syntax.

Instead of the ES6 way of doing things:

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

You can do this:

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

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

这篇关于如何在恢复功能之前等待JavaScript Promise解决?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆