How do you properly return multiple values from a promise?

后端 未结 9 1354
慢半拍i
慢半拍i 2020-12-04 14:14

I\'ve recently run into a certain situation a couple of times, which I didn\'t know how to solve properly. Assume the following code:

somethingAsync()
  .the         


        
相关标签:
9条回答
  • 2020-12-04 14:49

    Two things you can do, return an object

    somethingAsync()
        .then( afterSomething )
        .then( afterSomethingElse );
    
    function processAsync (amazingData) {
         //processSomething
         return {
             amazingData: amazingData, 
             processedData: processedData
         };
    }
    
    function afterSomething( amazingData ) {
        return processAsync( amazingData );
    }
    
    function afterSomethingElse( dataObj ) {
        let amazingData = dataObj.amazingData,
            processedData = dataObj.proccessedData;
    }
    

    Use the scope!

    var amazingData;
    somethingAsync()
      .then( afterSomething )
      .then( afterSomethingElse )
    
    function afterSomething( returnedAmazingData ) {
      amazingData = returnedAmazingData;
      return processAsync( amazingData );
    }
    function afterSomethingElse( processedData ) {
      //use amazingData here
    }
    
    0 讨论(0)
  • 2020-12-04 14:50

    Here is how I reckon you should be doing.

    splitting the chain

    Because both functions will be using amazingData, it makes sense to have them in a dedicated function. I usually do that everytime I want to reuse some data, so it is always present as a function arg.

    As your example is running some code, I will suppose it is all declared inside a function. I will call it toto(). Then we will have another function which will run both afterSomething() and afterSomethingElse().

    function toto() {
        return somethingAsync()
            .then( tata );
    }
    

    You will also notice I added a return statement as it is usually the way to go with Promises - you always return a promise so we can keep chaining if required. Here, somethingAsync() will produce amazingData and it will be available everywhere inside the new function.

    Now what this new function will look like typically depends on is processAsync() also asynchronous?

    processAsync not asynchronous

    No reason to overcomplicate things if processAsync() is not asynchronous. Some old good sequential code would make it.

    function tata( amazingData ) {
        var processed = afterSomething( amazingData );
        return afterSomethingElse( amazingData, processed );
    }
    
    function afterSomething( amazingData ) {
        return processAsync( amazingData );
    }
    function afterSomethingElse( amazingData, processedData ) {
    }
    

    Note that it does not matter if afterSomethingElse() is doing something async or not. If it does, a promise will be returned and the chain can continue. If it is not, then the result value will be returned. But because the function is called from a then(), the value will be wrapped into a promise anyway (at least in raw Javascript).

    processAsync asynchronous

    If processAsync() is asynchronous, the code will look slightly different. Here we consider afterSomething() and afterSomethingElse() are not going to be reused anywhere else.

    function tata( amazingData ) {
        return afterSomething()
            .then( afterSomethingElse );
    
        function afterSomething( /* no args */ ) {
            return processAsync( amazingData );
        }
        function afterSomethingElse( processedData ) {
            /* amazingData can be accessed here */
        }
    }
    

    Same as before for afterSomethingElse(). It can be asynchronous or not. A promise will be returned, or a value wrapped into a resolved promise.


    Your coding style is quite close to what I use to do, that is why I answered even after 2 years. I am not a big fan of having anonymous functions everywhere. I find it hard to read. Even if it is quite common in the community. It is as we replaced the callback-hell by a promise-purgatory.

    I also like to keep the name of the functions in the then short. They will only be defined locally anyway. And most of the time they will call another function defined elsewhere - so reusable - to do the job. I even do that for functions with only 1 parameter, so I do not need to get the function in and out when I add/remove a parameter to the function signature.

    Eating example

    Here is an example:

    function goingThroughTheEatingProcess(plenty, of, args, to, match, real, life) {
        return iAmAsync()
            .then(chew)
            .then(swallow);
    
            function chew(result) {
                return carefullyChewThis(plenty, of, args, "water", "piece of tooth", result);
            }
    
            function swallow(wine) {
                return nowIsTimeToSwallow(match, real, life, wine);
            }
    }
    
    function iAmAsync() {
        return Promise.resolve("mooooore");
    }
    
    function carefullyChewThis(plenty, of, args, and, some, more) {
        return true;
    }
    
    function nowIsTimeToSwallow(match, real, life, bobool) {
    }
    

    Do not focus too much on the Promise.resolve(). It is just a quick way to create a resolved promise. What I try to achieve by this is to have all the code I am running in a single location - just underneath the thens. All the others functions with a more descriptive name are reusable.

    The drawback with this technique is that it is defining a lot of functions. But it is a necessary pain I am afraid in order to avoid having anonymous functions all over the place. And what is the risk anyway: a stack overflow? (joke!)


    Using arrays or objects as defined in other answers would work too. This one in a way is the answer proposed by Kevin Reid.

    You can also use bind() or Promise.all(). Note that they will still require you to split your code.

    using bind

    If you want to keep your functions reusable but do not really need to keep what is inside the then very short, you can use bind().

    function tata( amazingData ) {
        return afterSomething( amazingData )
            .then( afterSomethingElse.bind(null, amazingData) );
    }
    
    function afterSomething( amazingData ) {
        return processAsync( amazingData );
    }
    function afterSomethingElse( amazingData, processedData ) {
    }
    

    To keep it simple, bind() will prepend the list of args (except the first one) to the function when it is called.

    using Promise.all

    In your post you mentionned the use of spread(). I never used the framework you are using, but here is how you should be able to use it.

    Some believe Promise.all() is the solution to all problems, so it deserves to be mentioned I guess.

    function tata( amazingData ) {
        return Promise.all( [ amazingData, afterSomething( amazingData ) ] )
            .then( afterSomethingElse );
    }
    
    function afterSomething( amazingData ) {
        return processAsync( amazingData );
    }
    function afterSomethingElse( args ) {
        var amazingData = args[0];
        var processedData = args[1];
    }
    

    You can pass data to Promise.all() - note the presence of the array - as long as promises, but make sure none of the promises fail otherwise it will stop processing.

    And instead of defining new variables from the args argument, you should be able to use spread() instead of then() for all sort of awesome work.

    0 讨论(0)
  • 2020-12-04 14:53

    Whatever you return from a promise will be wrapped into a promise to be unwrapped at the next .then() stage.

    It becomes interesting when you need to return one or more promise(s) alongside one or more synchronous value(s) such as;

    Promise.resolve([Promise.resolve(1), Promise.resolve(2), 3, 4])
           .then(([p1,p2,n1,n2]) => /* p1 and p2 are still promises */);
    

    In these cases it would be essential to use Promise.all() to get p1 and p2 promises unwrapped at the next .then() stage such as

    Promise.resolve(Promise.all([Promise.resolve(1), Promise.resolve(2), 3, 4]))
           .then(([p1,p2,n1,n2]) => /* p1 is 1, p2 is 2, n1 is 3 and n2 is 4 */);
    
    0 讨论(0)
提交回复
热议问题