Javascript 你如何正确地从一个承诺中返回多个值?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28703625/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 02:16:41  来源:igfitidea点击:

How do you properly return multiple values from a promise?

javascriptpromiseq

提问by Der Hochstapler

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()
  .then( afterSomething )
  .then( afterSomethingElse )

function afterSomething( amazingData ) {
  return processAsync( amazingData );
}
function afterSomethingElse( processedData ) {
}

Now a situation might arise where I would want to have access to amazingDatain afterSomethingElse.

现在可能会出现我想要访问amazingDatain 的情况afterSomethingElse

One obvious solution would be to return an array or a hash from afterSomething, because, well, you can only return one value from a function. But I'm wondering if there is way to have afterSomethingElseaccept 2 parameters and invoke it likewise, as that seems a lot easier to document and understand.

一个明显的解决方案是从 返回一个数组或一个散列afterSomething,因为,你只能从一个函数返回一个值。但我想知道是否有办法afterSomethingElse接受 2 个参数并同样调用它,因为这似乎更容易记录和理解。

I'm only wondering about this possibility since there is Q.spread, which does something similar to what I want.

我只是想知道这种可能性,因为有Q.spread,它的作用类似于我想要的。

回答by Benjamin Gruenbaum

You can't resolve a promise with multiple properties just like you can't return multiple values from a function. A promise conceptually represents a value over time so while you can represent composite values you can't put multiple values in a promise.

您无法解析具有多个属性的承诺,就像您无法从函数返回多个值一样。一个承诺在概念上代表了一个随时间变化的值,因此虽然您可以表示复合值,但您不能在一个承诺中放置多个值。

A promise inherently resolves with a single value - this is part of how Q works, how the Promises/A+ spec worksand how the abstractionworks.

Promise 固有地使用单个值解析——这是 Q 工作方式、Promises/A+ 规范如何工作以及抽象如何工作的一部分。

The closest you can get is use Q.spreadand return arrays or use ES6 destructuring if it's supported or you're willing to use a transpilation tool like BabelJS.

你能得到的最接近的是使用Q.spread和返回数组,或者使用 ES6 解构,如果它受支持或者你愿意使用像 BabelJS 这样的转译工具。

As for passing context down a promise chain please refer to Bergi's excellent canonical on that.

至于在承诺链中传递上下文,请参考Bergi 的优秀规范

回答by Alejandro Silva

you can only pass one value, but it can be an array with multiples values within, as example:

您只能传递一个值,但它可以是一个包含多个值的数组,例如:

function step1(){
  let server = "myserver.com";
  let data = "so much data, very impresive";
  return Promise.resolve([server, data]);
}

on the other side, you can use the destructuringexpression for ES2015 to get the individual values.

另一方面,您可以使用ES2015的解构表达式来获取各个值。

function step2([server, data]){
  console.log(server); // print "myserver.com"
  console.log(data);   // print "so much data, very impresive"
  return Promise.resolve("done");
}

to call both promise, chaining them:

调用两个 promise,将它们链接起来:

step1()
.then(step2)
.then((msg)=>{
  console.log(msg); // print "done"
})

回答by Kevin Reid

You can return an object containing both values — there's nothing wrong with that.

你可以返回一个包含这两个值的对象——这没有错。

Another strategy is to keepthe value, via closures, instead of passing it through:

另一种策略是通过闭包保留值,而不是通过:

somethingAsync().then(afterSomething);

function afterSomething(amazingData) {
  return processAsync(amazingData).then(function (processedData) {
    // both amazingData and processedData are in scope here
  });
}

Fully rather than partially inlined form (equivalent, arguably more consistent):

完全而不是部分内联形式(等效,可以说更一致):

somethingAsync().then(function (amazingData) {
  return processAsync(amazingData).then(function (processedData) {
    // both amazingData and processedData are in scope here
  });
}

回答by jemiloii

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
}

回答by jemiloii

Simply make an object and extract arguments from that object.

只需创建一个对象并从该对象中提取参数即可。

let checkIfNumbersAddToTen = function (a, b) {
return new Promise(function (resolve, reject) {
 let c = parseInt(a)+parseInt(b);
 let promiseResolution = {
     c:c,
     d : c+c,
     x : 'RandomString'
 };
 if(c===10){
     resolve(promiseResolution);
 }else {
     reject('Not 10');
 }
});
};

Pull arguments from promiseResolution.

从 promiseResolution 中提取参数。

checkIfNumbersAddToTen(5,5).then(function (arguments) {
console.log('c:'+arguments.c);
console.log('d:'+arguments.d);
console.log('x:'+arguments.x);
},function (failure) {
console.log(failure);
});

回答by Redu

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

无论您从承诺中返回什么,都将被包装到在下一.then()阶段解包的承诺中。

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 p1and p2promises unwrapped at the next .then()stage such as

在这些情况下,必须使用Promise.all()getp1p2promise 在下一.then()阶段解包,例如

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 */);

回答by gabriel

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.

因为这两个函数都将使用amazingData,所以将它们放在一个专用函数中是有意义的。每次我想重用一些数据时,我通常都会这样做,所以它总是作为函数 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().

由于您的示例正在运行一些代码,我假设它全部在函数中声明。我将称之为toto()。然后我们将有另一个函数,它将同时运行afterSomething()afterSomethingElse()

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

You will also notice I added a returnstatement 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 amazingDataand it will be available everywhere inside the new function.

你还会注意到我添加了一个return语句,因为它通常是 Promises 的方式——你总是返回一个 promise,这样我们就可以在需要时继续链接。在这里,somethingAsync()将产生amazingData并且它将在新函数内的任何地方可用。

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

现在这个新函数的外观通常取决于processAsync() 也是异步的吗?

processAsync not asynchronous

processAsync 不是异步的

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

如果processAsync()不是异步的,就没有理由让事情变得过于复杂。一些旧的好的顺序代码会成功。

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).

请注意,afterSomethingElse()是否正在执行异步操作并不重要。如果是这样,将返回一个承诺并且链可以继续。如果不是,则返回结果值。但是因为该函数是从then()调用的,所以无论如何该值都会被包装到一个承诺中(至少在原始 Javascript 中)。

processAsync asynchronous

processAsync 异步

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

如果processAsync()是异步的,代码看起来会略有不同。这里我们考虑afterSomething()afterSomethingElse()不会在其他任何地方重用。

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.

与之前的afterSomethingElse() 相同。它可以是异步的,也可以不是。将返回一个承诺,或者一个包含在已解决承诺中的值。



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-hellby a promise-purgatory.

您的编码风格与我过去所做的非常接近,这就是为什么我在 2 年后回答的原因。我不喜欢到处都有匿名函数。我觉得很难读。即使在社区中很常见。这是因为,我们更换了回调地狱诺-炼狱

I also like to keep the name of the functions in the thenshort. 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.

我也喜欢在then 中保留函数的名称。无论如何,它们只会在本地定义。大多数时候,他们会调用在别处定义的另一个函数——如此可重用——来完成这项工作。我什至对只有 1 个参数的函数这样做,所以当我向函数签名添加/删除参数时,我不需要进出函数。

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.

不要过分关注Promise.resolve()。这只是创建已解决承诺的一种快速方法。我试图通过这个实现的是让我在一个位置运行的所有代码 - 就在thens下方。具有更具描述性名称的所有其他函数都是可重用的。

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.

使用其他答案中定义的数组或对象也可以。这在某种程度上是Kevin Reid 提出答案

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

您还可以使用bind()Promise.all()。请注意,他们仍会要求您拆分代码。

using bind

使用绑定

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

如果你想保持你的函数可重用,但又不想让then里面的内容很短,你可以使用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.

为简单起见,bind()将在调用函数时将 args 列表(第一个除外)添加到函数中。

using Promise.all

使用 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.

在您的帖子中,您提到了spread()的使用。我从来没有使用过你正在使用的框架,但这里是你应该如何使用它。

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

有些人认为Promise.all()是所有问题的解决方案,所以我想它值得一提。

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.

您可以将数据传递给Promise.all()- 注意数组的存在 - 只要承诺,但确保没有任何承诺失败,否则它将停止处理。

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

而不是从args参数定义新变量,你应该能够使用spread()而不是then()来完成所有种类的很棒的工作。

回答by codelovesme

You can check Observablerepresented by Rxjs, lets you return more than onevalue.

您可以检查可观察到代表通过Rxjs,让你回到一个以上的价值。