提问人:Tobias Mühl 提问时间:4/28/2016 最后编辑:Tobias Mühl 更新时间:2/17/2023 访问量:160931
如何从 Promise 中提取数据
How to extract data out of a Promise
问:
我有一个返回数据的承诺,我想将其保存在变量中。由于异步性质,这在 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。
答:
不,您不能像您在示例中建议的那样从 promise 中同步获取数据。数据必须在回调函数中使用。或者,在函数式编程风格中,可以将 promise 数据映射()覆盖。
如果你可以使用 async/await(你应该很棒),那么你可以编写看起来同步的代码,但保留 promise 的异步性(参见@loganfsmyth注释)。
const { foo, bar } = await iAmAPromise.then(result => result.data);
总的来说,既然你已经在使用 ES6,我假设你也在使用转译器。在这种情况下,您绝对应该尝试一下 async/await。 请务必在决定中加权,因为今天它们尚未获得批准的规范。
评论
Promise.then
await
await
await
虽然你可以从异步函数中等待的 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
你期望那里发生什么?有两种可能性,而且都是坏的。
- 您的整个程序将不得不停止并等待 Promise 执行 在它知道“foo”和“bar”会是什么之前......不,可能是。(这是 在异步函数中,“await”实际上所做的是:它暂停 整个函数执行,直到值可用或抛出错误)
- foo 和 bar 只是未定义(这实际上是 发生),因为同步执行时,它们只是 顶级 Promise 对象(它本身不是“值”)的不存在属性, 而是一个准一元包装器,用于获取最终值或错误)其中大多数 可能甚至还不包含值。
评论
如果你愿意的话,我有一个解决方案,可以“取出”这个值。这是一种在后端将多个文件上传到 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);
评论
您需要做的就是使用 .then 提取您承诺中的所有内容
yourFunction().then( resp => {
... do what you require here
let var1 = resp.var1;
let var2 = resp.var2;
...
.....
})
yourFunction() 应该返回一个 Promise
评论
如何从承诺中获得价值
是的!您可以从承诺中提取价值!
不要让这里的任何人说你不能。只需意识到存储返回的 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 数据。
评论
let out; mypromise.then(x => out = x); console.log(out)
仅在以下情况下使用此代码
- 您正在手动调试,
- 你知道这个承诺已经成功了
此代码的行为:
- 虽然承诺尚未解决,但将是.
out
undefined
- 一旦 promise 解析为失败,就会引发错误。
- 当 promise 解析为成功时(可能在 console.log 之后),value 将从 Promise 结果更改为 Promise 结果 — 可能在您正在执行的操作中。
out
undefined
在生产代码中,或者实际上任何没有你运行的代码中,使用 Promise 结果的代码应该在回调中,而不是使用某个变量。那边:.then()
out
- 您的代码不会运行得太早(当结果仍未定义时),
- 并且不会运行得太晚(因为您不需要“我认为睡 10 秒钟应该可以”的解决方法),
- 并且不会在承诺失败时错误地运行。
评论
async/await
来了!const
let
errorHandler