Can anyone recommend a pattern for instantly retrieving data from a function that returns a Promise?
有人能推荐一种模式,可以从返回承诺的函数中立即检索数据吗?
My (simplified) example is an AJAX preloader:
我的(简化的)示例是AJAX预加载程序:
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.
如果这是下载一个大的页面,我希望能够检查发生了什么,并可能在稍后使用XHR abort()取消进程。
My loadPage function used to (before Promises) return an id that let me do this later:
我的loadPage函数过去(在承诺之前)返回一个id,让我以后可以这样做:
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.
在我新的基于承诺的版本中,我假设cancelLoadPage()将拒绝最初的loadPage()承诺。
I've considered a few options all of which I don't like. Is there a generally accepted method to achieve this?
我考虑了一些我不喜欢的选择。是否有一种普遍接受的方法来实现这一点?
6 个解决方案
#1
4
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).
对不起,这里的答案是:“不要使用承诺”。ES6承诺有三种可能的状态(作为用户):挂起、解析和拒绝(名称可能略有变化)。
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.
没有办法让你看到一个承诺的“内部”,看看已经做了什么,还有什么没有做——至少在本地ES6承诺的情况下不会。在承诺通知上做了一些有限的工作(在其他框架中),但是那些没有进入到ES6规范中,所以即使你找到了它的实现,使用它也是不明智的。
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.
对于您来说,同步地从异步调用中获取一些值是不安全的,特别是在JavaScript中。这样做的一个主要原因是,一个好的API如果可以是异步的,那么它将始终是异步的。
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.
这是因为,尽管promisee .resolve()是一个同步调用,它可以解决已经解析的承诺,但是()始终是异步的;这是规范的保证之一,也是一个很好的保证,因为它使代码更容易推理——想象一下如果您尝试混合同步和异步承诺会发生什么。
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.
顺便说一句,这适用于所有异步调用:JavaScript中任何可能是异步的操作都是异步的。因此,您无法在JavaScript提供的任何API中进行任何类型的同步内省。
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.
但这很快就会变得非常混乱,你仍然需要使用某种异步回调来实现它。当您尝试使用Fetch时,这一切都失败了。还请注意,承诺取消目前不是该规范的一部分。请参见这里了解关于该特定位的更多信息。
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.
TL:DR:同步内省在JavaScript的任何异步操作上都是不可能的,如果您尝试的话,承诺也不是正确的。例如,您无法同步显示正在进行的请求的信息。在其他语言中,尝试这样做需要阻塞或竞争条件。
#2
2
Promises are beautiful. I don't think there is any reason that you can not handle this with promises. There are three ways that i can think of.
承诺是美丽的。我认为你没有任何理由不能用承诺来解决这个问题。我可以想到三种方法。
- The simplest way to handle this is within the executer. If you would like to cancel the promise (like for instance because of timeout) you just define a
timeout
flag in the executer and turn it on with asetTimeout(_ => timeout = true, 5000)
instruction and resolve or reject only if timeout is false. ie (!timeout && resolve(res)
or!timeout && reject(err)
) This way your promise indefinitely remains unresolved in case of a timeout and youronfulfillment
andonreject
functions at thethen
stage never gets called. - 最简单的处理方法是在执行程序中。如果您想取消这个承诺(比如因为超时),您只需在执行程序中定义一个超时标志,并使用setTimeout(_ => timeout = true, 5000)指令打开它,并仅在timeout为false时解析或拒绝。ie(!超时&解析(res)或!超时&拒绝(err
- The second is very similar to the first but instead of keeping a flag you just invoke
reject
at the timeout with proper error description. And handle the rest at thethen
orcatch
stage. - 第二个与第一个非常相似,但是您只需在超时时使用正确的错误描述调用reject,而不是保留一个标志。然后在接下来的阶段处理剩下的部分,或者在接下去的阶段。
- However if you would like to carry the id of your asych operation to the sync world then you can also do it as follows;
- 但是,如果您想将asych操作的id带到同步世界,那么您也可以这样做;
In this case you have to promisify the async function yourself. Lets take an example. We have an async function to return the double of a number. This is the function
在这种情况下,您必须自己对async函数进行声明。让我们举一个例子。我们有一个异步函数来返回数字的双精度。这是函数
function doubleAsync(data,cb){
setTimeout(_ => cb(false, data*2),1000);
}
We would like to use promises. So normally we need a promisifier function which will take our async function and return another function which when run, takes our data and returns a promise. Right..? So here is the promisifier function;
我们希望使用承诺。通常我们需要一个promisifier函数它会接收异步函数并返回另一个函数当运行时,会接收我们的数据并返回一个承诺。对. . ?这是promisifier函数;
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
Lets se how they work together;
让我们看看他们是如何一起工作的;
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
function doubleAsync(data,cb){
setTimeout(_ => cb(false, data*2),1000);
}
var doubleWithPromise = promisify(doubleAsync);
doubleWithPromise(100).then(v => console.log("The asynchronously obtained result is: " + v));
So now you see our doubleWithPromise(data)
function returns a promise and we chain a then
stage to it and access the returned value.
现在你可以看到,我们的doubleWithPromise(数据)函数返回一个承诺,然后我们将一个stage链接到它并访问返回的值。
But what you need is not only a promise but also the id
of your asynch function. This is very simple. Your promisified function should return an object with two properties; a promise and an id. Lets see...
但是您需要的不仅是一个承诺,还包括异步函数的id。这是非常简单的。您的提升函数应该返回一个具有两个属性的对象;一个承诺和一个身份证明。
This time our async function will return a result randomly in 0-5 secs. We will obtain it's result.id
synchronously along with the result.promise
and use this id to cancel the promise if it fails to resolve within 2.5 secs. Any figure on console log Resolves in 2501 msecs
or above will result nothing to happen and the promise is practically canceled.
这次异步函数将在0-5秒内随机返回结果。我们将得到它的结果。id与结果同步。承诺并使用这个id来取消承诺,如果承诺不能在2.5秒内解决。在2501 msecs或以上的控制台日志上的任何数字都不会导致任何事情发生,并且承诺实际上被取消。
function promisify(fun){
return function(data){
var result = {id:null, promise:null}; // template return object
result.promise = new Promise((resolve,reject) => result.id = fun(data, (err,res) => err ? reject(err) : resolve(res)));
return result;
};
}
function doubleAsync(data,cb){
var dur = ~~(Math.random()*5000); // return the double of the data within 0-5 seconds.
console.log("Resolve in " + dur + " msecs");
return setTimeout(_ => cb(false, data*2),dur);
}
var doubleWithPromise = promisify(doubleAsync),
promiseDataSet = doubleWithPromise(100);
setTimeout(_ => clearTimeout(promiseDataSet.id),2500); // give 2.5 seconds to the promise to resolve or cancel it.
promiseDataSet.promise
.then(v => console.log("The asynchronously obtained result is: " + v));
#3
1
Well. If using angular you can make use of the timeout
parameter used by the $http
service if you need to cancel and ongoing HTTP request.
好。如果使用角度,如果需要取消和正在进行的http请求,可以使用$http服务使用的超时参数。
Example in typescript:
打字文件的例子:
interface ReturnObject {
cancelPromise: ng.IPromise;
httpPromise: ng.IHttpPromise;
}
@Service("moduleName", "aService")
class AService() {
constructor(private $http: ng.IHttpService
private $q: ng.IQService) { ; }
doSomethingAsynch(): ReturnObject {
var cancelPromise = this.$q.defer();
var httpPromise = this.$http.get("/blah", { timeout: cancelPromise.promise });
return { cancelPromise: cancelPromise, httpPromise: httpPromise };
}
}
@Controller("moduleName", "aController")
class AController {
constructor(aService: AService) {
var o = aService.doSomethingAsynch();
var timeout = setTimeout(() => {
o.cancelPromise.resolve();
}, 30 * 1000);
o.httpPromise.then((response) => {
clearTimeout(timeout);
// do code
}, (errorResponse) => {
// do code
});
}
}
Since this approach already returns an object with two promises the stretch to include any synchronous operation return data in that object is not far.
由于这种方法已经返回一个具有两个承诺的对象,因此在该对象中包含任何同步操作返回数据的扩展并不遥远。
If you can describe what type of data you would want to return synchronously from such a method it would help to identify a pattern. Why can it not be another method that is called prior to or during your asynchronous operation?
如果您能够描述您希望从这种方法同步返回的数据类型,它将有助于识别模式。为什么不能在异步操作之前或异步操作期间调用另一个方法呢?
#4
1
You can kinda do this, but AFAIK it will require hacky workarounds. Note that exporting the resolve
and reject
methods is generally considered a promise anti-pattern (i.e. sign you shouldn't be using promises). See the bottom for something using setTimeout
that may give you what you want without workarounds.
你可以这么做,但这需要一些陈腐的变通方法。注意,导出解析和拒绝方法通常被认为是一种承诺反模式(例如,表明您不应该使用承诺)。请参阅底部的一些使用setTimeout的内容,它可以在不使用变通方法的情况下提供您想要的内容。
let xhrRequest = (path, data, method, success, fail) => {
const xhr = new XMLHttpRequest();
// could alternately be structured as polymorphic fns, YMMV
switch (method) {
case 'GET':
xhr.open('GET', path);
xhr.onload = () => {
if (xhr.status < 400 && xhr.status >= 200) {
success(xhr.responseText);
return null;
} else {
fail(new Error(`Server responded with a status of ${xhr.status}`));
return null;
}
};
xhr.onerror = () => {
fail(networkError);
return null;
}
xhr.send();
return null;
}
return xhr;
case 'POST':
// etc.
return xhr;
// and so on...
};
// can work with any function that can take success and fail callbacks
class CancellablePromise {
constructor (fn, ...params) {
this.promise = new Promise((res, rej) => {
this.resolve = res;
this.reject = rej;
fn(...params, this.resolve, this.reject);
return null;
});
}
};
let p = new CancellablePromise(xhrRequest, 'index.html', null, 'GET');
p.promise.then(loadPage).catch(handleError);
// times out after 2 seconds
setTimeout(() => { p.reject(new Error('timeout')) }, 2000);
// for an alternative version that simply tells the user when things
// are taking longer than expected, NOTE this can be done with vanilla
// promises:
let timeoutHandle = setTimeout(() => {
// don't use alert for real, but you get the idea
alert('Sorry its taking so long to load the page.');
}, 2000);
p.promise.then(() => clearTimeout(timeoutHandle));
#5
1
You can use fetch()
, Response.body.getReader()
, where when .read()
is called returns a ReadableStream
having a cancel
method, which returns a Promise
upon cancelling read of the stream.
您可以使用fetch()、Response.body.getReader(),在这里,当.read()被调用时,它将返回一个有取消方法的ReadableStream,该方法在取消对流的读取时返回一个承诺。
// 58977 bytes of text, 59175 total bytes
var url = "https://gist.githubusercontent.com/anonymous/"
+ "2250b78a2ddc80a4de817bbf414b1704/raw/"
+ "4dc10dacc26045f5c48f6d74440213584202f2d2/lorem.txt";
var n = 10000;
var clicked = false;
var button = document.querySelector("button");
button.addEventListener("click", () => {clicked = true});
fetch(url)
.then(response => response.body.getReader())
.then(reader => {
var len = 0;
reader.read().then(function processData(result) {
if (result.done) {
// do stuff when `reader` is `closed`
return reader.closed.then(function() {
return "stream complete"
});
};
if (!clicked) {
len += result.value.byteLength;
}
// cancel stream if `button` clicked or
// to bytes processed is greater than 10000
if (clicked || len > n) {
return reader.cancel().then(function() {
return "read aborted at " + len + " bytes"
})
}
console.log("len:", len, "result value:", result.value);
return reader.read().then(processData)
})
.then(function(msg) {
alert(msg)
})
.catch(function(err) {
console.log("err", err)
})
});
<button>click to abort stream</button>
#6
0
The method I am currently using is as follows:
我目前使用的方法如下:
var optionalReturnsObject = {};
functionThatReturnsPromise(dataToSend, optionalReturnsObject ).then(doStuffOnAsyncComplete);
console.log("Some instant data has been returned here:", optionalReturnsObject );
For me, the advantage of this is that another member of my team can use this in a simple way:
对我来说,这样做的好处是,我的另一个团队成员可以用一种简单的方式:
functionThatReturnsPromise(data).then(...);
And not need to worry about the returns object. An advanced user can see from the definitions what is going on.
不需要担心返回对象。高级用户可以从定义中看到正在发生的事情。
#1
4
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).
对不起,这里的答案是:“不要使用承诺”。ES6承诺有三种可能的状态(作为用户):挂起、解析和拒绝(名称可能略有变化)。
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.
没有办法让你看到一个承诺的“内部”,看看已经做了什么,还有什么没有做——至少在本地ES6承诺的情况下不会。在承诺通知上做了一些有限的工作(在其他框架中),但是那些没有进入到ES6规范中,所以即使你找到了它的实现,使用它也是不明智的。
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.
对于您来说,同步地从异步调用中获取一些值是不安全的,特别是在JavaScript中。这样做的一个主要原因是,一个好的API如果可以是异步的,那么它将始终是异步的。
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.
这是因为,尽管promisee .resolve()是一个同步调用,它可以解决已经解析的承诺,但是()始终是异步的;这是规范的保证之一,也是一个很好的保证,因为它使代码更容易推理——想象一下如果您尝试混合同步和异步承诺会发生什么。
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.
顺便说一句,这适用于所有异步调用:JavaScript中任何可能是异步的操作都是异步的。因此,您无法在JavaScript提供的任何API中进行任何类型的同步内省。
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.
但这很快就会变得非常混乱,你仍然需要使用某种异步回调来实现它。当您尝试使用Fetch时,这一切都失败了。还请注意,承诺取消目前不是该规范的一部分。请参见这里了解关于该特定位的更多信息。
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.
TL:DR:同步内省在JavaScript的任何异步操作上都是不可能的,如果您尝试的话,承诺也不是正确的。例如,您无法同步显示正在进行的请求的信息。在其他语言中,尝试这样做需要阻塞或竞争条件。
#2
2
Promises are beautiful. I don't think there is any reason that you can not handle this with promises. There are three ways that i can think of.
承诺是美丽的。我认为你没有任何理由不能用承诺来解决这个问题。我可以想到三种方法。
- The simplest way to handle this is within the executer. If you would like to cancel the promise (like for instance because of timeout) you just define a
timeout
flag in the executer and turn it on with asetTimeout(_ => timeout = true, 5000)
instruction and resolve or reject only if timeout is false. ie (!timeout && resolve(res)
or!timeout && reject(err)
) This way your promise indefinitely remains unresolved in case of a timeout and youronfulfillment
andonreject
functions at thethen
stage never gets called. - 最简单的处理方法是在执行程序中。如果您想取消这个承诺(比如因为超时),您只需在执行程序中定义一个超时标志,并使用setTimeout(_ => timeout = true, 5000)指令打开它,并仅在timeout为false时解析或拒绝。ie(!超时&解析(res)或!超时&拒绝(err
- The second is very similar to the first but instead of keeping a flag you just invoke
reject
at the timeout with proper error description. And handle the rest at thethen
orcatch
stage. - 第二个与第一个非常相似,但是您只需在超时时使用正确的错误描述调用reject,而不是保留一个标志。然后在接下来的阶段处理剩下的部分,或者在接下去的阶段。
- However if you would like to carry the id of your asych operation to the sync world then you can also do it as follows;
- 但是,如果您想将asych操作的id带到同步世界,那么您也可以这样做;
In this case you have to promisify the async function yourself. Lets take an example. We have an async function to return the double of a number. This is the function
在这种情况下,您必须自己对async函数进行声明。让我们举一个例子。我们有一个异步函数来返回数字的双精度。这是函数
function doubleAsync(data,cb){
setTimeout(_ => cb(false, data*2),1000);
}
We would like to use promises. So normally we need a promisifier function which will take our async function and return another function which when run, takes our data and returns a promise. Right..? So here is the promisifier function;
我们希望使用承诺。通常我们需要一个promisifier函数它会接收异步函数并返回另一个函数当运行时,会接收我们的数据并返回一个承诺。对. . ?这是promisifier函数;
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
Lets se how they work together;
让我们看看他们是如何一起工作的;
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
function doubleAsync(data,cb){
setTimeout(_ => cb(false, data*2),1000);
}
var doubleWithPromise = promisify(doubleAsync);
doubleWithPromise(100).then(v => console.log("The asynchronously obtained result is: " + v));
So now you see our doubleWithPromise(data)
function returns a promise and we chain a then
stage to it and access the returned value.
现在你可以看到,我们的doubleWithPromise(数据)函数返回一个承诺,然后我们将一个stage链接到它并访问返回的值。
But what you need is not only a promise but also the id
of your asynch function. This is very simple. Your promisified function should return an object with two properties; a promise and an id. Lets see...
但是您需要的不仅是一个承诺,还包括异步函数的id。这是非常简单的。您的提升函数应该返回一个具有两个属性的对象;一个承诺和一个身份证明。
This time our async function will return a result randomly in 0-5 secs. We will obtain it's result.id
synchronously along with the result.promise
and use this id to cancel the promise if it fails to resolve within 2.5 secs. Any figure on console log Resolves in 2501 msecs
or above will result nothing to happen and the promise is practically canceled.
这次异步函数将在0-5秒内随机返回结果。我们将得到它的结果。id与结果同步。承诺并使用这个id来取消承诺,如果承诺不能在2.5秒内解决。在2501 msecs或以上的控制台日志上的任何数字都不会导致任何事情发生,并且承诺实际上被取消。
function promisify(fun){
return function(data){
var result = {id:null, promise:null}; // template return object
result.promise = new Promise((resolve,reject) => result.id = fun(data, (err,res) => err ? reject(err) : resolve(res)));
return result;
};
}
function doubleAsync(data,cb){
var dur = ~~(Math.random()*5000); // return the double of the data within 0-5 seconds.
console.log("Resolve in " + dur + " msecs");
return setTimeout(_ => cb(false, data*2),dur);
}
var doubleWithPromise = promisify(doubleAsync),
promiseDataSet = doubleWithPromise(100);
setTimeout(_ => clearTimeout(promiseDataSet.id),2500); // give 2.5 seconds to the promise to resolve or cancel it.
promiseDataSet.promise
.then(v => console.log("The asynchronously obtained result is: " + v));
#3
1
Well. If using angular you can make use of the timeout
parameter used by the $http
service if you need to cancel and ongoing HTTP request.
好。如果使用角度,如果需要取消和正在进行的http请求,可以使用$http服务使用的超时参数。
Example in typescript:
打字文件的例子:
interface ReturnObject {
cancelPromise: ng.IPromise;
httpPromise: ng.IHttpPromise;
}
@Service("moduleName", "aService")
class AService() {
constructor(private $http: ng.IHttpService
private $q: ng.IQService) { ; }
doSomethingAsynch(): ReturnObject {
var cancelPromise = this.$q.defer();
var httpPromise = this.$http.get("/blah", { timeout: cancelPromise.promise });
return { cancelPromise: cancelPromise, httpPromise: httpPromise };
}
}
@Controller("moduleName", "aController")
class AController {
constructor(aService: AService) {
var o = aService.doSomethingAsynch();
var timeout = setTimeout(() => {
o.cancelPromise.resolve();
}, 30 * 1000);
o.httpPromise.then((response) => {
clearTimeout(timeout);
// do code
}, (errorResponse) => {
// do code
});
}
}
Since this approach already returns an object with two promises the stretch to include any synchronous operation return data in that object is not far.
由于这种方法已经返回一个具有两个承诺的对象,因此在该对象中包含任何同步操作返回数据的扩展并不遥远。
If you can describe what type of data you would want to return synchronously from such a method it would help to identify a pattern. Why can it not be another method that is called prior to or during your asynchronous operation?
如果您能够描述您希望从这种方法同步返回的数据类型,它将有助于识别模式。为什么不能在异步操作之前或异步操作期间调用另一个方法呢?
#4
1
You can kinda do this, but AFAIK it will require hacky workarounds. Note that exporting the resolve
and reject
methods is generally considered a promise anti-pattern (i.e. sign you shouldn't be using promises). See the bottom for something using setTimeout
that may give you what you want without workarounds.
你可以这么做,但这需要一些陈腐的变通方法。注意,导出解析和拒绝方法通常被认为是一种承诺反模式(例如,表明您不应该使用承诺)。请参阅底部的一些使用setTimeout的内容,它可以在不使用变通方法的情况下提供您想要的内容。
let xhrRequest = (path, data, method, success, fail) => {
const xhr = new XMLHttpRequest();
// could alternately be structured as polymorphic fns, YMMV
switch (method) {
case 'GET':
xhr.open('GET', path);
xhr.onload = () => {
if (xhr.status < 400 && xhr.status >= 200) {
success(xhr.responseText);
return null;
} else {
fail(new Error(`Server responded with a status of ${xhr.status}`));
return null;
}
};
xhr.onerror = () => {
fail(networkError);
return null;
}
xhr.send();
return null;
}
return xhr;
case 'POST':
// etc.
return xhr;
// and so on...
};
// can work with any function that can take success and fail callbacks
class CancellablePromise {
constructor (fn, ...params) {
this.promise = new Promise((res, rej) => {
this.resolve = res;
this.reject = rej;
fn(...params, this.resolve, this.reject);
return null;
});
}
};
let p = new CancellablePromise(xhrRequest, 'index.html', null, 'GET');
p.promise.then(loadPage).catch(handleError);
// times out after 2 seconds
setTimeout(() => { p.reject(new Error('timeout')) }, 2000);
// for an alternative version that simply tells the user when things
// are taking longer than expected, NOTE this can be done with vanilla
// promises:
let timeoutHandle = setTimeout(() => {
// don't use alert for real, but you get the idea
alert('Sorry its taking so long to load the page.');
}, 2000);
p.promise.then(() => clearTimeout(timeoutHandle));
#5
1
You can use fetch()
, Response.body.getReader()
, where when .read()
is called returns a ReadableStream
having a cancel
method, which returns a Promise
upon cancelling read of the stream.
您可以使用fetch()、Response.body.getReader(),在这里,当.read()被调用时,它将返回一个有取消方法的ReadableStream,该方法在取消对流的读取时返回一个承诺。
// 58977 bytes of text, 59175 total bytes
var url = "https://gist.githubusercontent.com/anonymous/"
+ "2250b78a2ddc80a4de817bbf414b1704/raw/"
+ "4dc10dacc26045f5c48f6d74440213584202f2d2/lorem.txt";
var n = 10000;
var clicked = false;
var button = document.querySelector("button");
button.addEventListener("click", () => {clicked = true});
fetch(url)
.then(response => response.body.getReader())
.then(reader => {
var len = 0;
reader.read().then(function processData(result) {
if (result.done) {
// do stuff when `reader` is `closed`
return reader.closed.then(function() {
return "stream complete"
});
};
if (!clicked) {
len += result.value.byteLength;
}
// cancel stream if `button` clicked or
// to bytes processed is greater than 10000
if (clicked || len > n) {
return reader.cancel().then(function() {
return "read aborted at " + len + " bytes"
})
}
console.log("len:", len, "result value:", result.value);
return reader.read().then(processData)
})
.then(function(msg) {
alert(msg)
})
.catch(function(err) {
console.log("err", err)
})
});
<button>click to abort stream</button>
#6
0
The method I am currently using is as follows:
我目前使用的方法如下:
var optionalReturnsObject = {};
functionThatReturnsPromise(dataToSend, optionalReturnsObject ).then(doStuffOnAsyncComplete);
console.log("Some instant data has been returned here:", optionalReturnsObject );
For me, the advantage of this is that another member of my team can use this in a simple way:
对我来说,这样做的好处是,我的另一个团队成员可以用一种简单的方式:
functionThatReturnsPromise(data).then(...);
And not need to worry about the returns object. An advanced user can see from the definitions what is going on.
不需要担心返回对象。高级用户可以从定义中看到正在发生的事情。