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

查看:26
本文介绍了如何在恢复功能之前等待 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() {返回新的承诺(功能(解决,拒绝){$("#output").append("start");设置超时(功能(){解决();}, 1000);}).那么(函数(){$("#output").append("中间");返回结束";});};函数 getResultFrom(promise) {//去做返回结束";}var promise = kickOff();var 结果 = 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().所以,你根本无法做到你所要求的.相反,它具有异步操作,可以完成它们的工作,然后在它们完成时调用您(正如您一直在使用 Promise 那样).

部分原因是 Javascript 的单线程.如果单个线程正在旋转,则在该旋转线程完成之前,其他 Javascript 都无法执行.ES6 引入了 yield 和生成器,它们将允许一些类似的合作技巧,但我们距离能够在大量已安装的浏览器中使用它们还有很长的路要走(它们可以在某些服务器中使用)侧开发,您可以在其中控制正在使用的 JS 引擎).


仔细管理基于 Promise 的代码可以控制许多异步操作的执行顺序.

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

function kickOff() {返回新的承诺(功能(解决,拒绝){$("#output").append("start");设置超时(功能(){解决();}, 1000);}).那么(函数(){$("#output").append("middle");返回结束";});}启动().然后(功能(结果){//这里使用结果$("#output").append(result);});

这将以有保证的顺序返回输出 - 像这样:

开始中间结尾

2018 年更新(撰写此答案三年后):

如果您在支持 asyncawait 等 ES7 特性的环境中转译您的代码或运行您的代码,您现在可以使用 await 使您的代码出现"等待承诺的结果.它仍然是用 Promise 进行编程.它仍然不会阻止所有 Javascript,但它确实允许您以更友好的语法编写顺序操作.

而不是 ES6 的做事方式:

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

你可以这样做:

//返回一个承诺异步函数 wrapperFunc() {尝试 {让 r1 = 等待 someFunc();让 r2 = 等待 someFunc2(r1);//现在处理 r2返回一些值;//这将是返回的承诺的解析值}赶上(e){控制台日志(e);扔e;//让调用者知道 Promise 因这个原因被拒绝}}wrapperFunc().then(result => {//得到最终结果}).catch(err => {//出错});

async 函数在第一个 await 在其函数体内被命中后立即返回一个承诺,因此对于调用者来说,异步函数仍然是非阻塞的,调用者必须仍然处理返回的承诺并从该承诺中获得结果.但是,在 async 函数中,您可以在 Promise 上使用 await 编写更多类似顺序的代码.请记住,await 仅在您等待承诺时才会做一些有用的事情,因此为了使用 async/await,您的异步操作必须全部基于承诺.

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

async functions return a promise as soon as the first await is hit inside their function body so to the caller an async function is still non-blocking and the caller must still deal with a returned promise and get the result from that promise. But, inside the async function, you can write more sequential-like code using await on promises. Keep in mind that await only does something useful if you await a promise so in order to use async/await, your asynchronous operations must all be promise-based.

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

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