JS承诺 - 立即从返回Promise的函数中检索一些数据 [英] JS Promise - instantly retrieve some data from a function that returns a Promise

查看:148
本文介绍了JS承诺 - 立即从返回Promise的函数中检索一些数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我的(简化的)例子是一个AJAX预加载器:

  loadPage(index.html)。then(displayPage); 

如果这是下载大页面,我希望能够检查发生了什么并且可能取消在后续阶段使用XHR abort()进行处理。

我的loadPage函数用于(在Promises之前)返回一个ID,以便我稍后执行此操作:

  var loadPageId = loadPage(index.html,displayPage); 
...
doSomething(loadPageId);
cancelLoadPage(loadPageId);

在我的基于Promise的新版本中,我会想象cancelLoadPage()会拒绝loadPage()Promise。



我考虑了一些我不喜欢的选项。有没有一个普遍接受的方法来实现这一点?

解决方案

好的,我们先解决您的赏金注意事项。
$ b


[希望我可以将这些分数授予给那些说不要使用承诺的人...]


对不起,但这里的答案是:不要使用承诺。 ES6承诺有三种可能的状态(作为用户):等待,已解决和拒绝(名称可能稍微偏离)。



无法看到内部承诺看看已经完成了什么以及没有完成什么 - 至少不会与原生ES6承诺有关。 在承诺通知上完成了一些有限的工作(在其他框架中),但那些没有进入ES6规范的人,所以即使你找到了实现它也是不明智的。



承诺意味着代表未来某个时候的异步操作;独立的,它不适合这个目的。你想要的可能更类似于事件发布者 - 甚至是异步的,而不是同步的。



没有安全的方式让你同步从异步调用中获得一些价值,尤其是在JavaScript中。其中一个主要原因是,如果一个好的API可以是异步的,它将始终是异步的。



考虑下面的例子:

  const promiseValue = Promise.resolve(5)
promiseValue.then((value)=> console.log(value))
console.log('test')

现在假设这个承诺(因为我们知道提前的价值)是同步解决的。你期望看到什么?你会期望看到:

 > 5 
>测试

然而,实际情况是这样的:

 >测试
> 5

这是因为即使 Promise.resolve()是一个解析已解析的Promise的同步调用, then() always 为异步;这是规范的保证之一,并且它是一个很好的保证,因为它使代码更容易推理 - 想象一下,如果尝试混合使用同步和异步promise,会发生什么。



顺便说一下,这适用于所有异步调用:JavaScript中可能是异步的任何操作都将是异步的。因此,您无法在JavaScript提供的任何API中进行任何类型的同步自检。



这并不是说您无法做出某种类型的包装一个请求对象,如下所示:

 函数makeRequest(url){
const requestObject = new XMLHttpRequest()
const result = {

}

result.done = new Promise((resolve,reject)=> {
requestObject.onreadystatechange = function (){
..
}
})
requestObject.open(url)
requestObject.send()
return requestObject
}

但是这非常麻烦,很快,您仍然需要使用某种异步回调这工作。当您尝试使用 Fetch 时,这一切都会下降。另请注意,Promise取消目前不是规范的一部分。有关该特定位的更多信息,请参见此处。 TL:DR:在JavaScript中的任何异步操作中都不可能实现同步自省,如果您甚至尝试使用Promise,则Promise不是一种可行的方法。例如,您无法同步显示关于正在进行的请求的信息。在其他语言中,尝试这样做需要阻塞或竞争条件。


Can anyone recommend a pattern for instantly retrieving data from a function that returns a Promise?

My (simplified) example is an AJAX preloader:

loadPage("index.html").then(displayPage);

If this is downloading a large page, I want to be able to check what's happening and perhaps cancel the process with an XHR abort() at a later stage.

My loadPage function used to (before Promises) return an id that let me do this later:

var loadPageId = loadPage("index.html",displayPage);
...
doSomething(loadPageId);
cancelLoadPage(loadPageId);

In my new Promise based version, I'd imagine that cancelLoadPage() would reject() the original loadPage() Promise.

I've considered a few options all of which I don't like. Is there a generally accepted method to achieve this?

解决方案

Okay, let's address your bounty note first.

[Hopefully I'll be able to grant the points to someone who says more than "Don't use promises"... ]

Sorry, but the answer here is: "Don't use promises". ES6 Promises have three possible states (to you as a user): Pending, Resolved and Rejected (names may be slightly off).

There is no way for you to see "inside" of a promise to see what has been done and what hasn't - at least not with native ES6 promises. There was some limited work (in other frameworks) done on promise notifications, but those did not make it into the ES6 specification, so it would be unwise of you to use this even if you found an implementation for it.

A promise is meant to represent an asynchronous operation at some point in the future; standalone, it isn't fit for this purpose. What you want is probably more akin to an event publisher - and even that is asynchronous, not synchronous.

There is no safe way for you to synchronously get some value out of an asynchronous call, especially not in JavaScript. One of the main reasons for this is that a good API will, if it can be asynchronous, will always be asynchronous.

Consider the following example:

const promiseValue = Promise.resolve(5)
promiseValue.then((value) => console.log(value))
console.log('test')

Now, let's assume that this promise (because we know the value ahead of time) is resolved synchronously. What do you expect to see? You'd expect to see:

> 5
> test

However, what actually happens is this:

> test
> 5

This is because even though Promise.resolve() is a synchronous call that resolves an already-resolved Promise, then() will always be asynchronous; this is one of the guarantees of the specification and it is a very good guarantee because it makes code a lot easier to reason about - just imagine what would happen if you tried to mix synchronous and asynchronous promises.

This applies to all asynchronous calls, by the way: any action in JavaScript that could potentially be asynchronous will be asynchronous. As a result, there is no way for you do any kind of synchronous introspection in any API that JavaScript provides.

That's not to say you couldn't make some kind of wrapper around a request object, like this:

function makeRequest(url) {
  const requestObject = new XMLHttpRequest()
  const result = {

  }

  result.done = new Promise((resolve, reject) => {
    requestObject.onreadystatechange = function() {
      ..
    }
  })
  requestObject.open(url)
  requestObject.send()
  return requestObject
}

But this gets very messy, very quickly, and you still need to use some kind of asynchronous callback for this to work. This all falls down when you try and use Fetch. Also note that Promise cancellation is not currently a part of the spec. See here for more info on that particular bit.

TL:DR: synchronous introspection is not possible on any asynchronous operation in JavaScript and a Promise is not the way to go if you were to even attempt it. There is no way for you to synchronously display information about a request that is on-going, for example. In other languages, attempting to do this would require either blocking or a race condition.

这篇关于JS承诺 - 立即从返回Promise的函数中检索一些数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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