Fulfill (not allow) a promise with another promise

I want to fulfill a promise with some other promises. The thing is, I really want to access the second promise (still pending) as soon as the first promise is fulfilled. Unfortunately, it seems to me that I can get a second promise resolution value after both promises are executed.

Here is a usage example that I mean:

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 (eg, // 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 pickFile function displays a file collector where the user can select a file either from his hard drive or from a URL. It returns a picker promise that is executed as soon as the user selects a file. At this point, we still have to download the selected file over the network. Therefore, I cannot execute picker with the selected file as the permission value. Instead, picker should be executed with another promise of downloadProgress , which in turn will ultimately be executed with the selected file.

For completeness, here is 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 solve it. the difference between execution and resolution is that the permit check first checks to see if the p2 value already set is a promise. If so, then p1 will be delayed until p2 does d, and then p1 will be executed with p2 permission value instead of p2 .

I could get around this problem by creating a wrapper around p2 , i.e. replacing the string

  resolveP1(p2); // <--- PROBLEM: I actually want to *fulfill* p1 with p2 

from the second code example using

  resolveP1({promise: p2}); 

Then in the first code example, I would have to replace the line

  return downloadProgress; 

by

  return downloadProgress.promise; 

But that seems a bit hacked when all I really want to do is just fulfill (instead of resolving) the promise.

I would be grateful for any suggestions.

+8
source share
3 answers

There seems to be no solution other than the workaround described in the question. For future reference, if you want to fulfill (rather than resolve) the p promise with the value val , where val is another promise, and then just calling the promise resolution function for p with the val argument will not work properly. This will cause p be “locked” in the state val , so that p will execute with the permission value val once val (see spec ).

Instead, wrap val in another object and allow p with this 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 the type of val , you feel like you're out of luck. Either you should always transfer the promise resolution values ​​to another object, or you can try to determine if the val field then type of "function" and assures it conditionally.

However, in some cases, by default, resolving a promise may indeed have the desired effect. Therefore, only use the workaround described above if you are sure you want to fulfill instead of resolving the first promise with the second.

+2
source

Although different people use different terms, in the general terminology “to fulfill” means to make a promise in a “successful” state (as opposed to a “rejection”) - a state in which then handlers will be launched that hang it.

In other words, you cannot “fulfill” a promise with a promise. You can execute it with a value. (By the way, the term "permission" usually means either fulfillment or rejection.)

What you can do is return the promise from the .then handler, and this will entail replacing the original promise with the returned promise.

Here is a simple example:

 asyncTask1 . then(asyncTask2) . then(processData) 

where asyncTask1 is the promise, and asyncTask2 is the function that returns the promise. Therefore, when asyncTask1 is executed (successful), asyncTask2 is asyncTask2 , and the promise returned by .then is “canceled” by the promise of asyncTask2 , so that when it ends, the data can be processed.

I can do something like this by calling Promise.resolve with a promise as a parameter. This is a little wrong, because I do not allow the promise in a technical sense. Instead, a new promise has appeared that is “populated” with the promise I made. This is also useless because using the result is just like using the promise I made:

 Promise.resolve(asyncTask2) 

behaves exactly like

 asyncTask2 

(Assuming that asyncTask2 is already a promise, otherwise Promise.resolve has the effect of creating a promise that immediately executes with the passed value.)

Just as you can make a promise to Promise.resolve , you can pass the promise to the resolve function, provided to you as a callback parameter of the promise constructor. If the parameter you pass to resolve does not promise, the promise immediately matches that value. However, if the parameter that you switch to resolve is another promise, that promise takes over the body of the promise you are building. In other words, the promise you are building begins to behave exactly the same as the promise passed to resolve .

By "behave exactly" I mean that if the promise that you pass to resolve is already fulfilled, the promise that you build instantly fulfills the same value. If the promise you pass to resolve is already rejected, the promise you are building is immediately rejected for the same reason. If the promise that you pass to resolve is not yet resolved, then any then handlers will be processed with which you hang the promise that you build if and when the promise that you pass to resolve resolved.

Just as it is confusing that Promise.resolve can lead to a promise that is not actually resolved, it is also confusing that calling the resolve function passed to you as a parameter to the promise constructor may not actually allow the construction of the promise. if you call it an unresolved promise. Instead, as I said a couple of times, this leads to the fact that the promise is built in a state of complete congruence with the promise passed to resolve .

Therefore, if I miss the point of your question, the pickfile can 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 understand your question very clearly, so this may not be what you want. Please clarify if you want.

0
source

I found a similar solution during the transition from Angular $ q to the built-in Promise function. Promise.all can be an option (in the case of independent parallel asynchronous tasks) by transferring around the corresponding object or something decorated with a state, passing it to everything that will be ready when necessary. In the example of Promise.all below, notice how it is restored in one of the promises, and I figured out how to redirect the result of the chain. The result is just the return of the last promise. Although this does not answer the title of the question, using return Promise.reject(<an-object-including-a-promise>) (or permissions) gives a series and / or group of asynchronous sharing and control tasks along the way. In the case of a choice, downloading, then working with the file, I would process the processing of progress events: pickFile.then(download,orFailGracefully) with downloadProgress , processed in the on download onResolve handler (the downloading process does not seem to be an asynchronous task). The following 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; }); 
0
source

All Articles