Resolved promise waits for inner promise to be resolved - javascript

I'd like to have a promise to a promise, something like this:
let first = new Promise(resolveFirst => {
setTimeout(() => {
resolveFirst("resolved!");
}, 2000)
});
let second = new Promise(resolveSecond => {
setTimeout(() => {
resolveSecond(first);
}, 10)
});
second.then(secondValue => {
console.log("second value: ", secondValue);
secondValue.then(firstValue => {
console.log("first value: ", firstValue);
});
});
So that the console.log("second value: ", secondValue); is printed after 10 millies and then the console.log("first value: ", firstValue) will be printed 2000 afterwards.
What happens though is that I get:
second value: resolved!
And an error:
Uncaught (in promise) TypeError: secondValue.then is not a function
Together after 2010 millies.
It seems that when the second promise is resolved, and the first one is returned then it automatically waits for this first promise to resolve as well.
Why is that? And how do I break between them?
Edit
Here's a solution that was posted on facebook using Array.reduce():
const runPromisesInSeries =
ps => ps.reduce((p, next) =>
p.then(next), Promise.resolve());
const delay = d => new Promise(r => setTimeout(r, d));
runPromisesInSeries([() => delay(1000), () => delay(2000)]);
//executes each promise sequentially, taking a total of 3 seconds to complete
(https://www.facebook.com/addyosmaniofficial/photos/a.10151435123819601.1073741825.129712729600/10155386805609601/?type=3&theater)

By design, a promise that is resolved with a second promise takes on the value and state of the 2nd promise after the 2nd promise becomes settled (fulfilled or rejected), waiting for it to be settled as necessary.
It is not possible to fulfill a promise with a promise or thenable object (any object with a then method`) value unless the promise library is seriously flawed (older versions of JQuery would fulfill their own promise objects with a promise object from a different library).
Promise rejection does not have the same checks in place and will happily pass a promise object down the chain without waiting for it to be resolved. So you could reject the 2nd promise with the first and pick it up in a catch clause.
Although technically feasible I strongly advise against doing so except to prove it can be done - it's a maintenance issue for a third party trying to understand the code.
While you could pass the promise as an object property down the success channel of a promise chain, re-analysis of promise composition may provide a better or more standard solution. E.G. Promise.all waits for for two or more independent promises to be fulfilled before proceeding on a common task.

That's just part of their charm: If your promise A resolves to a thenable B, then A resolves only after B resolves, and A takes B's resolved value. That's part of both Promises/A+ and ES6, as part of the "promise resolution procedure".
You can see some of the advantages of that if (say) one action requires an action before its completion (like login), or requires loading another page of results, or has retry logic of its own.
Though it's not very idiomatic, if you want to return an unresolved promise immediately without waiting for it, you can do so by passing it in an {object} or [array], but there might not be much reason for that: Other than waiting for its completion, what would you do with a returned promise?

You can chain then only to a promise.
secondValue is not a promise, it's just the value that returned when you called the second promise. or the resolved value from that promise.
If you want that to work, try this:
let first = new Promise(resolveFirst => {
setTimeout(() => {
resolveFirst("resolved!");
}, 2000)
});
let second = new Promise(resolveSecond => {
setTimeout(() => {
resolveSecond(first);
}, 10)
});
second.then(secondValue => {
console.log("second value: ", secondValue);
/** first is a Promise. return a promise, and the next "then"
* will pass the result of that Promise.
*/
return first
})
.then(firstValue => {
console.log(firstValue)
})
EDIT 1
Further explanation for why second promise resolved the eventual value (string) of the first promise, and not the promise itself, can be found on mdn:
Promise.resolve()
if the value was a promise, that object becomes the result of the call to Promise.resolve; otherwise the returned promise will be fulfilled with the value.

Related

Why a resolved promise still in pending state?

Example:
let a = Promise.resolve("123")
.then(val => {
console.log(a);
return 100;
})
.then(val => {
console.log(val);
console.log(a);
})
.finally(() => {
console.log(a);
});
Output:
Promise { <pending> }
100
Promise { <pending> }
Promise { <pending> }
Why a resolved promise still in pending state?
So, a contains the result of executing all of this:
let a = Promise.resolve(...).then(...).then(...).finally(...);
Each step of that phase creates a new promise and a contains the last one in the chain (the last promise is the one that .finally() returns). Each time you log a, you're logging the final promise and is has not yet been resolved until the chain is done.
Keep in mind that all these .then() and .finally() methods execute immediately and each returns a new promise. When they execute, they register the callbacks passed to them in a way that those callbacks can be called later when the promise they are associated with resolves or rejects. Even though the first promise is immediately resolved, it still doesn't execute it's .then() handlers until after this whole chain of .then().then().finally() executes and all the promise in that chain are created and all the handlers specified in that chain are registered. So, a contains the fourth promise that .finally() returns, not the promise that Promise.resolve() creates and that fourth promise is not resolved until after all the handlers in your chain have finished executing. Since you're doing the logging from all those handlers, that's why that last promise in the chain still shows as pending everywhere you're logging it.
If you do things differently like this:
let d = Promise.resolve("123");
d.then(val => {
console.log(d);
return 100;
}).then(val => {
console.log(val);
console.log(d);
}).finally(() => {
console.log(d);
});
Then, you will see this:
Promise { '123' }
100
Promise { '123' }
Promise { '123' }
Because now d represents the first promise in the chain, not the end promise in the chain so it is indeed resolved when all the other handlers execute.
So, probably the operative part here that you were not aware of is that each .then(), .catch() or .finally() returns a new promise that is hooked to the chain in front of it. Your original console.log(a) was monitoring only the very last promise in the chain, not the first promise in the chain or not the current promise in the chain.

Is promise.all used to de-capsulate promise value?

I'm trying to understand what Promise.all() is doing exactly.
As far as I understand,
promise is something which holds a return value for asynchronous execution in javascript.
So the values encapsulated(?) by promise is not directly accessible in sychronoous running.
(Terminology warning! I'm going to use analogy to show how I see promise and promise.all())
To get a direct access to the promisified values,
I need to peel off a shell called promise, which is
promise.all at the end of asynchronous function.
after this 'peeling-off', I could access to a synchronously available values.
Am I understanding Promise.all correctly?
Thank you so much.
Example is from mdn website.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// expected output: Array [3, 42, "foo"]
all the promises and just synchronous value (const promise2) were successfully approached by synchronous execution 'console.log()'.
If I don't peel off the promise shell like below,
Promise.all([promise2, promise3]).then((values) => {
console.log(values);
console.log(promise1)
});
The values that js spits out are as below.
Array [42, "foo"]
[object Promise]
Please feel free to criticize my analogy and
let me know if I understand something wrong.
Thank you in advance.
Your analogy is a very interesting one, but I'm sure it's a common perception of how Promises work. Promises are constructs that manage deffered values - values that will be at some stage, time-dependent. Using the term "wrapping" makes it sound like the value is already there, when in fact it most likely isn't.. yet.
A promise can be used to represent the response from an API, or perhaps a lookup from a database - both of these take time - something synchronous code can't handle. You might make a request, and get a promise in response. You can then use this promise as a helper to get the value you intended to receive.
When you call promise.then(value => ...), the then function is waiting until the value is ready. Inside the promise, something must call resolve(value) before the then function will fire (note that the promise can also be rejected via reject, in which case promise.catch would be fired). Here's a full example:
const promise = new Promise(resolve => {
setTimeout(() => {
resolve(10);
}, 2000);
});
promise.then(value => {
// This fires after about 2 seconds
console.log(value); // 10
});
So the trick here is to visualise that getting results from a promise occurs on a different "thread".. perhaps in a callback or after an await call. Also, once a promise has been resolved, calling then a second time will return the same value as previously, and it will be faster, but still not instant as promises "values" are always asynchronously returned.
Promise.all waits until all the provided promises have resolved. What is returned by Promise.all is an array containing all of the resolved results in order. So if you have the following:
const promise1 = Promise.resolve(12); // Asynchronously returns 12
const promise2 = new Promise(resolve => {
setTimeout(() => {
resolve(10);
}, 2000);
});
Promise.all([promise1, promise2]).then(results => {
// results will be [12, 10], and will fire after about 2 seconds
});
// here, even though we're after the promise assignments,
// the values to BOTH of the promises are still not yet defined (not resolved)
TLDR; A promise is a class that records a value once its provided function resolves. The then method will call its callback with the value once it is set by the promise function (by calling resolve or reject).

Promises chaining, wrong order

I'd like to understand the promises chaining in Javascript. So I wrote this little fiddle:
https://jsfiddle.net/GarfieldKlon/89syq4fh/
It works like intended. c waits for b, and b waits for a.
But when I change these lines to:
a().then( result => console.log(result))
.then( () => {b().then( result => console.log(result))})
.then( () => {c().then( result => console.log(result))});
So I added the curly braces around b and c and then the output is 1, 3, 2. And I don't understand why.
When I add a return then it works again:
a().then( result => console.log(result))
.then( () => {return b().then( result => console.log(result))})
.then( () => {return c().then( result => console.log(result))});
But why? When there are no curly braces you're only allowed to write one statement, correct? And this statement is implicitly returned?
And when you use curly braces you have to explicitly return something?
But why does it mess up the order when using curly braces without return? And why does it still log something when the return is missing?
When there are no curly braces you're only allowed to write one expression which is implicitly returned?
And when you use curly braces you have to explicitly return something?
Yes.
But why does it mess up the order when using curly braces without return? And why does it still log something when the return is missing?
Because the promise then function relies on return values for chaining.
Remember that then returns a new promise for the result of the callback. When that result is another promise, it waits for the result of that inner promise before fulfilling the returned promise - the one on which you are chaining the second then() call.
If your callback starts b().then(…) but returns undefined, the next step in the chain (the c() call) doesn't wait for b to finish.
When using the arrow syntax, you can interpret it in many things:
() => 'hello'
is equivalent to
() => { return 'hello'; }
However, when doing
() => {'hello'}
as similar to what you wrote:
.then( () => {b().then( result => console.log(result))})
then you can interpret that as
() => {'hello'; return;}
So, in the code, yours promise handling b().then( result => console.log(result)) is called before a void return; is done. You are not returning the resulting promise object at all. It gets called when b is finished, no matter of what you were doing.
This has to do with arrow functions. When you omit the curly braces, the return is implicit. So
const f = () => 42;
is equivalent to
const f = () => { return 42; };
Also, b().then(something).then(somenthingElse) is still only one expression, so it can be implicitly returned.
A Promise is an object that represents the result of an asynchronous operation.
It is important to remember that Promises are not just abstract ways registering callbacks to run when some async code finishes — they represent the results of that async code.
When we write a chain of .then() invocations, we are not registering multiple callbacks on a single Promise object. Instead, each invocation of the then() method returns a new Promise object.
That new Promise object is not fulfilled until the function passed to then() is complete. The value that fulfills promise 1 becomes the input to the callback2() function. The callback performs some computation and returns a value v. When the callback returns the value, the promise is resolved with the value v.
When a Promise is resolved with a value that is not itself a Promise, it is immediately fulfilled with that value.
It is important to understand in this case, so I rephrase: if a callback returns a non-Promise, that return value becomes the value of the promise, and the promise is fulfilled.
If the return value v is itself a Promise, then the promise is resolved but not yet fulfilled.
Your case:
allow me to post the functions a,b and c here:
let a = function() {
return new Promise( (resolve, reject) => {
setTimeout(() => resolve(1), 300);
});
}
let b = function() {
return new Promise( (resolve, reject) => {
setTimeout(() => resolve(2), 200);
});
}
let c = function() {
return new Promise( (resolve, reject) => {
setTimeout(() => resolve(3), 100);
});
}
and the implementation
a().then( result => console.log(result))
.then( () => {b().then( result => console.log(result))})
.then( () => {c().then( result => console.log(result))});
Here is what is happening
By adding the curly braces to the arrow function body, we no longer get the automatic return.
This function now returns undefined instead of returning a Promise, which means that the current Promise is fulfilled and next stage in this Promise chain will be invoked with undefined as its input.
So, function a gets invoked, returns a promise, which is, once fulfilled invokes the callback1.
callback 1 returnes undefined so the promise1 is fulfilled and the next .then() invokes the callback2.
The callback2 invokes the b which itself returnes a Promise, but this Promise is not returned by the callback2. The callback2 returns undefined. The Promise2 is fulfilled with undefined and life goes on - Callback3 gets invoked.
At this point b and c are running simultaneously, and because c is 100ms faster than b you get the 1,3,2 order.
Terminology
We say that the promise has been fulfilled if and when the first callback (argument of then method) is called.
And we say that the Promise has been rejected if and when the second callback (argument of then method) is called.
If a Promise is neither fulfilled nor rejected, then it is pending. And once a promise is fulfilled or rejected, we say that it is settled.
If the async code runs normally (and the Promise is fulfilled), then that result is essentially the return value of the code.
If the Promise is fulfilled, then the value is a return value that gets passed to any callback functions registered as the first argument of then().
Promises can also be resolved. It is easy to confuse this resolved state with the fulfilled state or with settled state, but it is not precisely the same as either.
Resolved means that another Promise has been assigned to the value of the current Promise. The current Promise has become associated with, or “locked onto,” another Promise.
We don’t know yet whether the Promise will be fulfilled or rejected, because it is not yet settled. Its fate now depends entirely on what happens to Promise

Promise returning a promise [duplicate]

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

Fulfill (don't resolve) promise with another 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;
});

Categories

Resources