如何在恢复功能之前等待 JavaScript Promise 解决?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28921127/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 02:35:52  来源:igfitidea点击:

How to wait for a JavaScript Promise to resolve before resuming function?

javascriptasynchronouspromise

提问by dx_over_dt

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 Promisewhich sets the iFrame's onloadevent to call resolve(), sets the iFrame's src, and returns the promise.

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

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

所以,我可以只调用loadUrl(url).then(myFunc),它会在执行任何myFunc内容之前等待页面加载。

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).

我在我的测试中到处使用这种模式(不仅仅是用于加载 URL),主要是为了允许对 DOM 进行更改(例如,模仿单击按钮,并等待 div 隐藏和显示)。

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.

这种设计的缺点是我经常用几行代码编写匿名函数。此外,虽然我有一个变通方法(QUnit 的assert.async()),但定义Promise 的测试函数会在运行Promise之前完成。

I'm wondering if there is any way to get a value from a Promiseor 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.

我想知道是否有任何方法可以从 a 获取值Promise或等待(阻塞/睡眠)直到它解决,类似于 .NET 的IAsyncResult.WaitHandle.WaitOne(). 我知道 JavaScript 是单线程的,但我希望这并不意味着函数不能屈服。

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>

采纳答案by jfriend00

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.

我想知道是否有任何方法可以从 Promise 获取值或等待(阻塞/睡眠)直到它解决,类似于 .NET 的 IAsyncResult.WaitHandle.WaitOne()。我知道 JavaScript 是单线程的,但我希望这并不意味着函数不能屈服。

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).

在浏览器目前这一代的Javascript不具有wait()sleep()允许其他事情跑。所以,你根本不能做你所要求的。相反,它具有异步操作,它们将完成它们的工作,然后在它们完成时调用您(因为您一直在使用 promise)。

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 yieldand 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).

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



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

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

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:

我不确定我是否完全理解您要在代码中实现的顺序,但是您可以使用现有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);
});

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

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

start
middle
end

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

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

If you either transpile your code or run your code in an environment that supports ES7 features such as asyncand await, you can now use awaitto 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.

如果您转换代码或在支持 ES7 特性(例如async和 )的环境中运行代码await,您现在可以使用await使您的代码“出现”以等待承诺的结果。它仍在以承诺的方式发展。它仍然不会阻止所有 Javascript,但它确实允许您以更友好的语法编写顺序操作。

Instead of the ES6 way of doing things:

而不是 ES6 的做事方式:

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

回答by Josh Habdas

If using ES2016 you can use asyncand awaitand do something like:

如果使用 ES2016,您可以使用asyncandawait并执行以下操作:

(async () => {
  const data = await fetch(url)
  myFunc(data)
}())

If using ES2015 you can use Generators. If you don't like the syntax you can abstract it away using an asyncutility functionas explained here.

如果使用 ES2015,您可以使用Generators。如果你不喜欢的语法,你可以将它抽象掉使用async效用函数这里解释

If using ES5 you'll probably want a library like Bluebirdto give you more control.

如果使用 ES5,您可能需要像Bluebird这样的库来为您提供更多控制。

Finally, if your runtime supports ES2015 already execution order may be preserved with parallelism using Fetch Injection.

最后,如果您的运行时支持 ES2015,已经可以使用Fetch Injection以并行方式保留执行顺序。

回答by Stan Kurdziel

Another option is to use Promise.all to wait for an array of promises to resolve. Code below shows how to wait for all the promises to resolve and then deal with the results once they are all ready (as that seemed to be the objective of the question); However, start could obviously output before middle has resolved just by adding that code before it calls resolve.

另一种选择是使用 Promise.all 来等待一系列 promise 被解析。下面的代码显示了如何等待所有 promise 得到解决,然后在它们都准备好后处理结果(因为这似乎是问题的目标);但是, start 显然可以在 Middle 解析之前输出,只需在调用解析之前添加该代码即可。

function kickOff() {
  let start = new Promise((resolve, reject) => resolve("start"))
  let middle = new Promise((resolve, reject) => {
    setTimeout(() => resolve(" middle"), 1000)
  })
  let end = new Promise((resolve, reject) => resolve(" end"))

  Promise.all([start, middle, end]).then(results => {
    results.forEach(result => $("#output").append(result))
  })
}

kickOff()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>

回答by Guga Nemsitsveridze

You can do it manually. (I know, that that isn't great solution, but..) use whileloop till the resulthasn't a value

您可以手动完成。(我知道,这不是很好的解决方案,但是..)使用while循环直到result没有值

kickOff().then(function(result) {
   while(true){
       if (result === undefined) continue;
       else {
            $("#output").append(result);
            return;
       }
   }
});