如何从 Promise 中提取数据

How to extract data out of a Promise

提问人:Tobias Mühl 提问时间:4/28/2016 最后编辑:Tobias Mühl 更新时间:2/17/2023 访问量:160931

问:

我有一个返回数据的承诺,我想将其保存在变量中。由于异步性质,这在 JavaScript 中是不可能的吗,我是否需要用作回调?onResolve

我可以以某种方式使用它吗(例如,用async/await包装它):

const { foo, bar } = Promise.then(result => result.data, errorHandler);
// rest of script

而不是这个?

Promise.then(result => {
   const { foo, bar } = result.data;
   // rest of script
 }, errorHandler);

注意:使用 Bluebird 库而不是本机实现,我无法从 Promise 更改为 asnyc/await 或 Generators。

JavaScript 承诺 ecmascript-6 bluebird es6-promise

评论

0赞 Sandip Nirmal 4/28/2016
javascript 是可能的。您可以将在承诺成功时收到的数据分配给之前声明的变量。你的变量将具有你从 promise 中获得的值。您可以参考:developer.mozilla.org/en/docs/Web/JavaScript/Reference/...
0赞 Kyll 4/28/2016
async/await 来了!
1赞 Tobias Mühl 4/28/2016
@SandipNirmal 这是可能的,但可能会导致错误,因为 JavaScript 不会等待 Promise 解决。还需要更改为 ,这可能会产生副作用。constlet
0赞 CodingIntrigue 4/28/2016
仅供参考,这里并没有真正处理错误,它只是立即执行errorHandler
0赞 Tobias Mühl 4/28/2016
@RGraham 此语法适用于 bluebird.js 实现。请参阅此处的 bluebird API 参考 根据 Promises/A+ 标准,这也是正确的(参见第 2.2 段)

答:

57赞 silkAdmin 4/28/2016 #1

不,您不能像您在示例中建议的那样从 promise 中同步获取数据。数据必须在回调函数中使用。或者,在函数式编程风格中,可以将 promise 数据映射()覆盖

如果你可以使用 async/await(你应该很棒),那么你可以编写看起来同步的代码,但保留 promise 的异步性(参见@loganfsmyth注释)。

const { foo, bar }  = await iAmAPromise.then(result => result.data);

总的来说,既然你已经在使用 ES6,我假设你也在使用转译器。在这种情况下,您绝对应该尝试一下 async/await。 请务必在决定中加权,因为今天它们尚未获得批准的规范。

评论

1赞 a better oliver 4/28/2016
“太棒了”从什么时候开始,阻止很棒?
1赞 silkAdmin 4/28/2016
@zeroflagL异步并不比 promise 回调更阻塞。线程可以自由地处理其他东西(即,如果在 NodeJS 中的新请求),而一旦 promise 解决,执行将恢复。这只是一种不同的语法。
1赞 a better oliver 4/28/2016
函数的执行停止。它没有 .因此,如果你在浏览器中生成事件处理程序,那绝对不是很好。Promise.thenawait
6赞 loganfsmyth 4/29/2016
@zeroflagL 你错了,async/await 不会阻塞,它会执行 async 函数直到第一个,然后暂停 async 函数的执行,并将控制权返回给父作用域并返回一个 promise。当传递给 await 的值可用时,函数的执行将恢复。阻止执行和暂停执行之间有一个重要区别。await
3赞 a better oliver 4/29/2016
@loganfsmyth 我感到羞耻。我刚刚意识到我忘记了一个重要的点:你只能在异步函数中。我心里有常规函数,这将是同步的,因此会阻塞。await
2赞 Dtipson 4/30/2016
是的,你不能全局“等待”,只能在一个可以暂停和恢复的函数的上下文中(因为这一切都只是生成器之上的糖)。在函数外部,你仍然在处理一个 Promise,并且你不能在该上下文中“从”一个 Promise 中“得到一个最终的值,你只能在它上面映射/flatMap 来做事。
19赞 Dtipson 4/30/2016 #2

虽然你可以从异步函数中等待的 Promise 中获取值(仅仅是因为它暂停函数以等待结果),但你永远无法直接从 Promise 中“输出”一个值,并返回到与创建 Promise 本身的代码相同的作用域中。

这是因为“out of”意味着尝试获取未来存在的东西(最终解析的值),并将其放入过去已经发生的上下文(同步变量赋值)中。

也就是说,时间旅行。即使时间旅行是可能的,它也可能不是一个好的编码实践,因为时间旅行可能非常令人困惑:)

一般来说,如果你发现自己觉得需要这样做,这是一个好兆头,你需要重构一些东西。请注意,您在此处使用“result => result.data”执行的操作:

Promise.then(result => result.data, errorHandler);
// rest of script

..已经是你通过将值传递给函数来处理(从字面上看,映射)值的情况。但是,假设 “// script 的其余部分”做了一些与此值相关的重要操作,您可能希望继续使用另一个函数映射现在更新的值,然后该函数对该值执行一些副作用(例如在屏幕上显示数据)。

Promise
    .then(({ data }) => data)
    .then(data => doSomethingWithData(data))// rest of script
    .catch(errorHandler);

“doSomethingWithData”将在将来的某个未知时间点被调用(如果它曾经被调用过)。这就是为什么将所有这些行为清楚地封装到一个特定的函数中,然后将该函数挂接到 Promise 链上是一个很好的做法。

老实说,这种方式更好,因为它要求您清楚地声明将要发生的特定事件序列,从第一次运行到所有应用程序代码的执行中显式分离。

换句话说,想象一下这个场景,假设在全局顶级范围内执行:

const { foo, bar } = Promise.then(result => result.data, errorHandler);
console.log(foo);
//...more program

你期望那里发生什么?有两种可能性,而且都是坏的。

  1. 您的整个程序将不得不停止并等待 Promise 执行 在它知道“foo”和“bar”会是什么之前......不,可能是。(这是 在异步函数中,“await”实际上所做的是:它暂停 整个函数执行,直到值可用或抛出错误)
  2. foo 和 bar 只是未定义(这实际上是 发生),因为同步执行时,它们只是 顶级 Promise 对象(它本身不是“值”)的不存在属性, 而是一个准一元包装器,用于获取最终值或错误)其中大多数 可能甚至还包含值。

评论

1赞 Zeor137 6/2/2020
我有同样的问题。我理解异步问题。我正在努力获得的是,如果我需要一个异步函数来使用 await,如何让同步函数等待异步,并且我不明白如何在等待函数后从同步变量中获取一个值。
0赞 Dtipson 6/6/2020
你不能,真的。同步函数(以及全局范围内的代码)不会/不能等待异步函数。与原始示例一样,关键是:“//脚本的其余部分”位中实际上是什么。剧本的其余部分是什么?如果它曾经对变量 foo 和 bar 做一些事情,那么它们在脚本的“其余”中将只是未定义,因为它都将同步执行,而不是等待 Promise 返回。它们不仅未定义,而且始终保持未定义状态,因为该范围只是在一次传递中运行和完成。
0赞 Dtipson 6/6/2020
最终,async/await 改变了函数中运行的 WAY 代码:它使熟悉的分步同步语法的工作方式与通常不同。但这种逻辑最终只是以一种非基于回调的编写方式来表达回调的功能。好处是,我们可以在整个过程中使用相同的语法,而不需要那么多的嵌套,或者进入更高层次的概念,如类型。但正是由于这个原因,它令人困惑:它看起来相同,但根据不同的逻辑工作。
0赞 Zeor137 6/7/2020
当我试图找出去做我想做的事时,我就明白了。我认为对于我们这些菜鸟来说,一件很难的事情是如何让这两种功能协同工作。例如,我想到使用全局变量或返回值来使用我需要的异步函数中的值,而我必须使用其中的 setState() 函数。
1赞 heltonbiker 10/1/2020
“将所有这些行为清楚地封装到一个特定的函数中,然后将该函数挂接到 Promise 上是一种很好的做法”:这部分帮助我理解了如何正确组织这种类型的代码,谢谢。
0赞 Achinta Sharma 11/14/2022 #3

如果你愿意的话,我有一个解决方案,可以“取出”这个值。这是一种在后端将多个文件上传到 AWS S3 的方法,必须异步处理。我还需要来自 S3 的响应,因此我需要 Promise 中的值:

async function uploadMultipleFiles(files) {

const promises = [];    //Creating an array to store promises
for (i = 0; i < files.length; i++) {
    const fileStream = fs.createReadStream(files[i].path)

    const uploadParams = {
        Bucket: bucketName,
        Body: fileStream,
        Key: files[i].filename
    }

    promises.push(s3.upload(uploadParams).promise())  //pushing each promise instead 
                   //of awaiting, to enable for concurrent uploads.
}

await Promise.all(promises).then(values => {
    console.log("values: ", values) //just checking values
    result = values;  //storing in a different variable
});
return result; //returning that variable

}

与此处讨论的问题相关的关键行如下:

await Promise.all(promises).then(values => {
    console.log("values: ", values) //just checking values
    res = values;  //storing in a different variable
});
return res; //returning that variable

但是,当然,我们还必须在将调用此函数的函数中等待:

const result = await uploadMultipleFiles(files);

评论

0赞 Dtipson 12/28/2022
不过,这里的“当然”是问题所在。您正在使用异步函数,该函数在点击“await”时停止。不过,这是异步函数中的一项特殊功能,而不是“脱离”回调上下文的方法。另请注意,不需要在 Promise.all 行中使用额外的 .then 语句。如果你使用的是 await,那么结果是结果数组 const res = Promise.all(promises),或者甚至只是返回 Promise.all(promises) 有效。但这仍然是函数外部的,将是一系列结果的 Promise。
0赞 Diego Arbelaez 12/10/2022 #4

您需要做的就是使用 .then 提取您承诺中的所有内容


yourFunction().then( resp => {
... do what you require here

let var1 = resp.var1;
let var2 = resp.var2;
...
.....
})

yourFunction() 应该返回一个 Promise

评论

0赞 CryptoAlgorithm 12/16/2022
这并不能回答问题,因为它询问了如何将结果中的变量移出 .then 回调。
0赞 Dtipson 12/28/2022
但是你不能那样做。想一想,在这种情况下,将变量“移出”Promise 及其回调的上下文意味着什么。全局范围,大多数人正在考虑的“外部”只是逐行运行,所有这些都在单个线程中,然后就完成了。在异步和生成器函数的上下文之外,它只是运行到完成,而不会停止或犹豫未来的值。
-1赞 Stokely 12/27/2022 #5

如何从承诺中获得价值

是的!您可以从承诺中提取价值!

不要让这里的任何人说你不能。只需意识到存储返回的 promise 值的任何变量都可能会有短暂的延迟。因此,如果你有一个 JavaScript 脚本页面,需要 Promise 或 async-await 函数之外的数据,你可能必须创建循环、间隔计时器或事件侦听器,以等待一段时间后获取值。因为大多数 async-await-promise 都是 REST 调用,而且速度非常快,所以等待只需要一个快速的 while 循环!

这很简单!只需设置一个变量(或创建一个函数),该变量可以访问异步或承诺代码中的值,并将该值存储在您可以检查的外部变量、对象、数组等中。下面是一个原始示例:

    // I just created a simple global variable to store my promise message.
    var myDelayedData = '';

   // This function is only used to go get data.
   // Note I set the delay for 5 seconds below so you can test the delay
    const getData = () => {
      return new Promise((resolve, reject) => {
        setTimeout(() => resolve('my promise data'), 5000);
      });
    }

    // I like to create a second async function to get the data
    // from the promise object and save the data to my global variable.
    const processData = async () => {
       let data = await getData();
       // Save the delayed data to my global variable
       myDelayedData = data;
    }

    // Start the data call from the promise.
    processData();

    // Open up your browser, hit F12 to pull up the browser devtools
    // Click the "console" tab and watch the script print out
    // the value of the variable with empty message until after
    // 5 seconds the variable is assigned to the resolved promise
    // and apears in the message!

    // THAT IS IT! Your variable is assigned the promise value
    // after the delay I set above!

    // TEST: But let's test it and see...
    var end = setInterval(function(){
        console.log("My Result: " + myDelayedData);
        if(myDelayedData !== ''){
           clearInterval(end);
        }
    }, 1000);

    // You should see this in devtools console.
    // Each line below represents a 1 second delay.

    My Result: 
    My Result: 
    My Result: 
    My Result: my promise data

大多数看到这段代码的人会说:“那为什么要使用 Promise,只需调用数据、暂停并更新您的应用程序?True:Promise 的全部意义在于将数据进程封装在 promise 中,并在脚本的其余部分继续执行时执行操作。

但。。。您可能需要在 Promise 之外输出结果。例如,您可能有其他需要该数据的全局进程,因为它会更改全局应用程序的状态。但至少你知道,如果需要的话,你可以获得 Promise 数据。

评论

0赞 Dtipson 12/28/2022
因此,人们之所以说你不能从 Promise 中提取数据,本质上是为了提醒他们 Promise 在未来发生:从本质上讲,它们在语法上捕捉了只有将来才可用的值的效果。您在这里所做的就是设置一个单独的并行时间机器,该机器会定期检查在 Promise 中设置的全局值......而且在未来也是如此。
0赞 Stokely 12/28/2022
@Dtipson - 我同意你的观点,即承诺是一个单独的未来事件。但是我质疑这样一种观点,即程序的其他部分应该无法访问 Promise 结果。结果也应该能够更改应用程序的“状态”。未来不仅仅是一个主观的线性事件。事件不仅会影响您的时间线,还会影响许多其他人。:)
0赞 Dtipson 12/29/2022
您可以创建一些东西来管理状态,并将 Promise 值的结果存储在其中,这是绝对正确的。但这并不是人们所说的“输出”值的意思:他们想要的是异步函数提供的东西:命令式地将值提取到与 Promise 相同的范围内。这是不可能的,甚至不明智,因为在 await/async 函数之外,javascript 会逐行评估完成,而不是等待或暂停。
1赞 Dtipson 12/29/2022
这与人们可能熟悉的许多语言形成鲜明对比:例如,ruby 或 php,在这些语言中,标准库中的某些操作在完成之前不会执行下一行代码,无论它们需要多长时间。
2赞 Esteis 2/17/2023 #6
let out; mypromise.then(x => out = x); console.log(out)

仅在以下情况下使用此代码

  • 您正在手动调试,
  • 你知道这个承诺已经成功了

此代码的行为:

  • 虽然承诺尚未解决,但将是.outundefined
  • 一旦 promise 解析为失败,就会引发错误。
  • 当 promise 解析为成功时(可能在 console.log 之后),value 将从 Promise 结果更改为 Promise 结果 — 可能在您正在执行的操作中。outundefined

在生产代码中,或者实际上任何没有你运行的代码中,使用 Promise 结果的代码应该在回调,而不是使用某个变量。那边:.then()out

  • 您的代码不会运行得太早(当结果仍未定义时),
  • 并且不会运行得太晚(因为您不需要“我认为睡 10 秒钟应该可以”的解决方法),
  • 并且不会在承诺失败时错误地运行。