This question already has answers here:
Are JavaScript forever-pending promises bad?
(2 answers)
Closed 3 years ago.
The problem is like this
function demo() {
return new Promise((resolve, reject) => {
...
// The problem here!!
//I just found in some rare case we failed to call resolve or reject
})
}
demo()
.then(res => {
console.log('resolve')
console.log(res)
})
.catch(rej => {
console.log('reject')
console.log(rej)
})
.finally(() => {
console.log('why')
})
When I failed to call resolve or reject, even the finally block is not called! Why ?
I had thought it was a bug then I found the original author seemed to do that on purpose that if he did not call either resolve or reject, none of then/catch/finally should be called, i.e. in that case no follow-up action should be taken.
But is this a valid way to handle the situation that no follow-up action should be taken ? Will it cause any trouble ?
----- update -----
Even though my question was marked duplicated I am still not satisfied with the answers I got. Originally I had thought it was a bad idea to let promise stay in pending state forever.
But the answer in that SO said "There should be no side effect."
Does never resolved promise cause memory leak? also said "In short - at least in modern browsers - you don't have to worry about unresolved promises as long as you don't have external references to them". So it seems ok to let promise stay in pending if that is the purpose.
Internally, a promise can be in one of three states:
Pending, when the final value is not available yet. This is the only state that may transition to one of the other two states.
Fulfilled, when and if the final value becomes available. A fulfillment value becomes permanently associated with the promise. This may be any value, including undefined.
Rejected, if an error prevented the final value from being determined. A rejection reason becomes permanently associated with the promise. This may be any value, including undefined, though it is generally an Error object, like in exception handling.
https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Promise
In your case, the promise is in the pending state and calling demo function will always in wait for the promise status to be fulfilled or rejected.
A promise is always expected to either resolve or reject.
If you intend to do a no follow up, you may resolve with an empty dataset or reject with an error code that suits your use case.
You may use Promise.race to check if a promise was finished on time.
So if you forgot to call resolve or reject into your promise, then Promise.race still be resolved or rejected after delay.
var promise1 = new Promise(function(resolve, reject) {
setTimeout(reject, 500);
});
var promise2 = new Promise(function(resolve, reject) {
});
Promise.race([promise1, promise2]).then(function(value) {
console.log(value);
}).catch(err => console.log('promise rejected'));
JavaScript works strangely in the situation below.
I saw this answer and got this question about Javascript's weird behavior: https://stackoverflow.com/a/50173415/1614973
I found that if we change the then in his code to any other key name we will get a totally different result.
CodePen demo: his, changed
I tried Chrome and Firefox and they all have this problem.
I explorer the problem there and find some basic rules of this "bug".
// This one will always pending
const pendingPromise = Promise.resolve(x=>x).then(r => ({ then: y => 0 }));
pendingPromise.then(r => console.log("promise resolved")); // "promise resolved" will never logged
// Thanks #Jaromanda X's correction. a simpler version is:
const pendingPromise1 = Promise.resolve().then(() => ({then: y => 0}))
pendingPromise1.then(r => console.log("promise1 resolved")); // "promise1 resolved" will never logged
The pendingPromise is pending forever. There are three things to toggle this bug as far as I can see:
The original one must be fulfilled with a function. (unnecessary constraint)
In .then, an Object with a key of the name "then" must be returned.
The value of the key then must be a function.
I'd like to get any clue about why this happens.
The reason it never resolves is because your final "then" never calls resolve:
.then(r=>({then: y => 0})) just returns a non-thenable 0.
y is a resolve callback. To make your code work, change y to resolve and then call it. Or, just call y. Point is, until the resolve is called, the promise remains pending.
console.log("Starting Strange Promise")
const pendingPromise = Promise.resolve(x=>x).then(r=>(
{then: y => "Strange Promise Done!"}
));
pendingPromise.then(console.log) // never happens
console.log("Starting Hacked Promise")
const hackedPromise = Promise.resolve(x=>x).then(r=>(
{then: resolve => resolve("Hacked Promise Done!")}
));
hackedPromise.then(console.log) // happens, like, speed of light quickly
We are all familiar with this: in one of the .then(callback), if the callback returns another promise, say lastPromise, the whole promise chain (up till that point) will effectively "become" lastPromise.
Internally, the promise chain doesn't check if the lastPromise thing is a real promise at all. It only check if it implements the thenable interface (has a .then() method). If it does, then it further assumes the method also conforms to the spec.
A promise must provide a then method to access its current or eventual value or reason.
A promise’s then method accepts two arguments:
promise.then(onFulfilled, onRejected)
Now you return an object from the callback that has a .then method, which makes it a thenable. The promise chain will thus treat this object like a comrade, believe that it is a "promise", call its .then(onFulfilled, onRejected) (which is effectively the (resolve, reject) pair).
Now a good thenable knows the right thing to do with (onFulfilled, onRejected). Unfortunately they trust the wrong guy. Your implementation goes like:
then = (onFulfilled) => 0
You never actually call onFulfilled(someValue) to resolve from pending state, so since the whole chain now "becomes" lastPromise, but your fake lastPromise fails to resolve/reject, the whole chain just stucks at pending state for good.
That's the sad story of blindly trust any thenable to be a promise.
I want to fulfill a promise with some other promise. The point is that I really want to get access to the (still pending) second promise as soon as the first promise is fulfilled. Unfortunately, I only seem to be able to get the second promise's resolution value once both both promises are fulfilled.
Here's the use case that I have in mind:
var picker = pickFile();
picker.then( // Wait for the user to pick a file.
function(downloadProgress) {
// The user picked a file. The file may not be available just yet (e.g.,
// if it has to be downloaded over the network) but we can already ask
// the user some more questions while the file is being obtained in the
// background.
...do some more user interaction...
return downloadProgress;
}
).then( // Wait for the download (if any) to complete.
function(file) {
// Do something with the file.
}
)
The function pickFile displays a file picker where the user may pick a file either from their own hard drive or from a URL. It returns a promise picker that is fulfilled as soon as the user has picked a file. At this point, we may still have to download the selected file over the network. Therefore, I cannot fulfill picker with the selected file as resolution value. Instead, picker should be fulfilled with another promise, downloadProgress, which in turn will eventually be fulfilled with the selected file.
For completenes, here's a mock implementation of the pickFile function:
function pickFile() {
...display the file picker...
var resolveP1 = null;
var p1 = new Promise(
function(resolve, reject) {
resolveP1 = resolve;
}
);
// Mock code to pretend the user picked a file
window.setTimeout(function() {
var p2 = Promise.resolve('thefile');
resolveP1(p2); // <--- PROBLEM: I actually want to *fulfill* p1 with p2
}, 3000);
return p1;
}
The problem in the marked line is that I would like to fulfill the promise p1 with the new promise p2, but I only know how to resolve it. The difference between fulfilling and resolving is that resolving first checks if the supplied value p2 is again a promise. If it is, then fulfillment of p1 will be deferred until p2 is fulfilld, and then p1 will be fulfilled with p2's resolution value instead of p2 itself.
I could work around this issue by building a wrapper around p2, i.e. by replacing the line
resolveP1(p2); // <--- PROBLEM: I actually want to *fulfill* p1 with p2
from the second code example by
resolveP1({promise: p2});
Then, in the first code example, I'd have to replace the line
return downloadProgress;
by
return downloadProgress.promise;
But this seems like a bit of a hack when all I really want to do is just fulfill (instead of resolve) a promise.
I'd appreciate any suggestions.
There doesn't seem to be a solution apart from the workaround I already described in the question. For future reference, if you want to fulfill (rather than resolve) a promise p with a value val, where val is another promise, then just calling the promise resolution function for p with argument val won't work as expected. It would cause p to be "locked in" on the state of val, such that p will be fulfilled with val's resolution value once val is fulfilled (see spec).
Instead, wrap val in another object and resolve p with that object:
var resolveP; // Promise resolution function for p
var p = new Promise(
function(resolve, reject) {
resolveP = resolve;
}
);
function fulfillPwithPromise(val) { // Fulfills p with a promise val
resolveP({promise: val});
}
p.then(function(res) {
// Do something as soon as p is fulfilled...
return res.promise;
}).then(function(res) {
// Do something as soon as the second promise is fulfilled...
});
This solution works if you already know that val is a promise. If you cannot make any assumptions about val's type, then you seem to be out of luck. Either you have to always wrap promise resolution values in another object, or you can try to detect whether val has a field then of type "function" and wrap it conditionally.
That said, in some cases the default behavior of promise resolution may actually have the desired effect. So only use the workaround described above if you are sure that you want to fulfill instead of resolve the first promise with the second one.
Although different people use different terms, in common terminology, "fulfill" means to put a promise in the "success" state (as opposed to "reject")--the state that will trigger then then handlers hanging off it.
In other words, you cannot "fulfill" a promise with a promise. You can fulfill it with a value. (By the way, the term "resolve" is usually meant as either of fulfilling or rejecting.)
What you can do is return a promise from a .then handler and that will have the effect of essentially replacing the original promise with the returned promise.
Here is a simple example of doing that:
asyncTask1 . then(asyncTask2) . then(processData)
where asyncTask1 is a promise, and asyncTask2 is a function which returns a promise. So when asyncTask1 is fulfilled (done successfully), then asyncTask2 runs, and the promise returned by the .then is "taken over" by the promise asyncTask2 returns, so that when it finishes, the data can be processed.
I can do something similar by calling Promise.resolve with a promise as parameter. It's a bit of a misnomer, because I'm not resolving the promise in the technical sense. Instead, the new promise created is "inhabited" by the promise I passed in. It's also useless, because using the result is exactly the same as using the promise I passed in:
Promise.resolve(asyncTask2)
behaves exactly the same as
asyncTask2
(assuming asyncTask2 is already a promise; otherwise Promise.resolve has the effect of creating a promise which is immediately fulfilled with the passed in value.)
Just as you can pass a promise to Promise.resolve, you can pass a promise to the resolve function provided to you as a parameter of the promise constructor callback. If the parameter you pass to resolve is a non-promise, the promise immediately fulfills with that value. However, if the parameter you pass to resolve is another promise, that promise "takes over the body" of the promise you are constructing. To put it another way, the promise you are constructing starts to behave exactly as the the promise passed to resolve.
By "behave exactly" I mean, if the promise you pass in to resolve is already fulfilled, the promise you are constructing is instantly fulfilled with the same value. If the promise you pass in to resolve is already rejected, the promise you are constructing is instantly rejected with the same reason. If the promise you pass in to resolve is not resolved yet, then any then handlers you hang off the promise you are constructing will be invoked if and when the promise you pass to resolve is resolved.
Just as it is confusing that Promise.resolve may result in a promise which is not actually resolved, it is similarly confusing that calling the resolve function handed to you as a parameter to the promise constructor may not actually resolve the promise being constructed if you call it with an unresolved promise. Instead, as I've said a couple of times now, it has the effect of putting the promise being constructed in a state of total congruence with the promise passed to resolve.
Therefore, unless I am missing the point of your question, pickfile could be written as
function pickFile() {
return new Promise(function(resolve, reject) {
...display the file picker...
// Mock code to pretend the user picked a file
window.setTimeout(function() {
resolve('thefile');
});
}
I didn't really understand your question clearly, so this might not be what you want. Please clarify if you care to.
Found a similar solution in the process of moving away from Angular's $q to the native Promise feature. Promise.all could be an option (in cases of independent parallel async tasks) by passing around an appropriate object, or something decorated with the state, passing it off to whatever is ready when appropriate. In the Promise.all sample below note how it recovers in one of the promises--took me awhile to realize how to redirect the result of a chain. The result of the all is just the last promise's return. While this doesn't answer the question's title, using return Promise.reject(<an-object-including-a-promise>) (or resolve) gives a series and/or group of async tasks shared access and control along the way. In the case of picking, downloading then working with a file I'd take out the progress-event handling then do: pickFile.then(download,orFailGracefully) with downloadProgress handled within the download onResolve handler (download-progress doesn't appear to be an async task). Below are related experiments in the console.
var q = {
defer: function _defer(){
var deferred = { };
deferred.promise = new Promise(function(resolve, reject){
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
}
};
var communityThatCares = q.defer();
communityThatCares.promise.then(function(someGood){
console.log('someGood', someGood);
return someGood;
}, function(someBad){
console.warn('someBad', someBad);
return someBad;
});
(new Promise(function(resolve, reject){ communityThatCares.about = 'communityThatCares'; setTimeout(resolve,1000, communityThatCares); }))
.then(
function(e){
console.log(3,e); return e.resolve(e);
}, function(e){
console.warn(3, e); return e.reject(e);
});
var todo = {
find:'swan'
};
var greaterGood = [(
(new Promise(function(res,rej){ res(todo); })).then(function(e){ e.stuff = 'things'; return e; }),
(new Promise(function(res,reject){
reject(todo);
})).then(function(e){ return e; }
,function(e){
console.warn(1,e);
e.recover = 'uh oh';
return Promise.resolve(e);
}).then(function(e){ console.log(2,e); return e; }),
(new Promise(function(res,rej){ res(todo); })).then(function(e){ console.log(1,e); e.schedule = 'today'; return e; },function(e){ console.warn(1,e); return e; }).then(function(e){ console.log(2,e); return e; }))
];
var nkay = Promise.all( greaterGood )
.then(function(todo){
console.log('all',todo[0]); return todo;
}, function(todo){
console.warn('all',todo[0]); return todo;
});
I'm writing an Angular 2 RC5 application, and unit testing using Karma and Jasmine.
I have a method that returns a Promise<Foo> (It's on top of a call to angular's http.post) I want to run some assertions after that finishes.
Something like this doesn't work
let result = myService.getFoo();
result.then(rslt => expect(1+1).toBe(3)); // the error is lost
This creates an 'Unhandled Promise rejection' warning, but the error is suppressed and the test passes. How do I run assertions based on my resolved promise?
Notes:
The .catch() method doesn't seem to be what I'm after. I don't want to log or do anything that continues normal program flow, I want to fail the test.
I've seen code that looks like $rootScope.$digest();. I'm not sure what the typescript equivalent of this sort of thing is. There doesn't seem to be a way of saying: "I have a promise, I'm going to wait here until I have a synchronous result".
The test should look something like this:
it('should getFoo', function (done) {
let result = myService.getFoo();
result
.then(rslt => expect(rslt).toBe('foo'))
.then(done);
});
Using the done callback works, but you should be able to do this as well:
(Note the return)
it('should getFoo', function () {
let result = myService.getFoo();
return result
.then(rslt => expect(rslt).toBe('foo'))
});
I'm testing a library I wrote to throttle execution of a function.
The API is throttler.do(fn) and it returns a promise of fn's return value (resolved at whichever point the throttler decides it's ok to run it).
I'm using lolex to fake Date() and setTimeout so if I set the throttler to allow two actions per minute and do
throttler.do(() => {});
throttler.do(() => {});
throttler.do(() => {}).should.eventually.equal(5);
this fails as expected (it times out because it's waiting on the last promise forever since I never call lolex.tick).
Is there a way I can turn this into a passing test, something like
throttler.do(() => {}).should.never.be.fulfilled;
I can't do
setTimeout(() => done(), 1500)
because the setTimeout method is faked by lolex.
You can mock the Promise to be synchronous and then check the Promise state. Here is a library you can use promise-sync