如何在 Promise 之间异步传递数据?
Posted
技术标签:
【中文标题】如何在 Promise 之间异步传递数据?【英文标题】:How to pass data between promise asynchronously? 【发布时间】:2020-08-24 06:59:19 【问题描述】:我想从后端获取几个数据,并在加载时处理它们。现在,当这段代码运行时,data1 和 data2 是未定义的。我想等他们,但我不知道如何保持我的代码干净。我不确定 Promise.all() 是否适合这里,因为我需要专门保存 data1 和 data2 的值,而且我无法编写通用代码来解析 Promise.all()。
return new Promise( (resolve,reject) =>
let data1;
let data2;
let url1 = "http://fakeapi.com/getData1";
fetch(url1)
.then(res => res.json())
.then(data => data1SpecificAction())
.then(data => data1 = data)
.catch("cannot fetch data1")
let url2 = "http://fakeapi.com/getData2";
fetch(url2)
.then(res => res.json())
.then(data => data2 = data)
.catch("cannot fetch data2")
if(data1 && data2)
resolve()
else
reject()
我该如何解决这个问题?
【问题讨论】:
这个问题属于codereview.stackexchange.com 除了其他问题,你从来没有给data2
分配任何东西……是错字吗?
对于初学者来说,任何时候你从.then()
处理程序中为更高范围的变量赋值,你可能做错了什么。您将无法在需要时使用该更高范围的变量,因为仅在 .then()
处理程序内,因此您实际上知道它的值何时有效。
另外,我们可以更好地使用真实代码,而不是伪代码。
链接你的两个承诺或使用Promise.all()
或使用await
。这是你的三个选择。并且,摆脱您创建的new Promise()
。当您已经从 fetch()
获得异步操作的承诺时,就不需要这样做了。你正在做的事情包含几个反模式。
【参考方案1】:
您的问题或答案中不需要很多内容。你可以这样做:
const url1 = "http://fakeapi.com/getData1";
const url2 = "http://fakeapi.com/getData2";
return Promise.all([
fetch(url1).then(res => res.json()).then(data1SpecificAction),
fetch(url2).then(res => res.json())
]).then([data1, data2] =>
if (data1 && data2) return; // resolve
// reject with appropriate error
let msg = data1 ? "missing data2" : "missing data1";
throw new Error(msg);
);
你的问题和答案中不需要做的事情:
-
不要将现有的 Promise 包装在另一个手动创建的 Promise 中。这被认为是一种反模式,因为根本不需要手动创建的 Promise。你可以只返回你已经拥有的承诺。
不要将
.then()
结果分配给更高范围的变量。虽然偶尔会出现这种情况,但这不是其中之一,通常是您做错事的警告信号。
我发现fetch()
接口有一些烦恼,我发现它们经常受益于辅助函数(例如 404 状态解决,而不是拒绝):
function fetchJson(...args)
return fetch(...args).then(res =>
if (!res.ok) throw new Error(`Got $res.status status`);
return res.json();
);
const url1 = "http://fakeapi.com/getData1";
const url2 = "http://fakeapi.com/getData2";
return Promise.all([
fetchJson(url1).then(data1SpecificAction),
fetchJson(url2)
]).then([data1, data2] =>
if (data1 && data2) return; // resolve
// reject with appropriate error
let msg = data1 ? "missing data2" : "missing data1";
throw new Error(msg);
);
而且,在您想要拒绝结果错误的特定情况下,您甚至可以这样做:
function fetchJsonCheck(...args)
return fetch(...args).then(res =>
if (!res.ok) throw new Error(`Got $res.status status`);
return res.json();
).then(result =>
if (!result) throw new Error("empty result");
return result;
);
const url1 = "http://fakeapi.com/getData1";
const url2 = "http://fakeapi.com/getData2";
return Promise.all([
fetchJsonCheck(url1).then(data1SpecificAction),
fetchJsonCheck(url2)
]);
【讨论】:
@eelkejohnson - 您如何看待这些更简单的选项? 非常感谢!它真的帮助我度过了低谷。在我的真实情况下,我有 4 .then() + 1.catch() 用于第一个动作,2 .then() 和 1.catch() 用于第二个动作。这种方式更轻,因为您不需要将所有动作包装成更小的块。但就我而言,它确实提高了我的代码的可读性。这是我没有依赖的完整代码:pastebin.com/xPGwx0qg 总而言之,我了解了我正在编写的反模式,就我而言:1) 提高了可读性,2) 我不知道我可以像这样传递数据:) @EelkeJohnson - 仅供参考,您的exports.controlInstantIncident
函数包含一个承诺反模式。你不需要将所有这些都包装在一个承诺中。只需返回studentService.getStudentFromHash(...).then(...).then(...).catch(...)
。您在许多其他功能中重复该错误。您通过从最后一个.then()
返回的值来设置解析值。您通过从.catch()
抛出的内容设置拒绝原因。无需包装新的承诺。这是一个纯粹的 promise 反模式。返回您现有的承诺。不要用新的包装。
现在,我看到我在任何地方都像这样编写了我的 50k 行代码应用程序:O,并且我开始在只有 500 mb 内存的树莓派 3 型号 b 上运行我的应用程序。我广泛使用的这种反模式会导致浏览器出现内存问题吗?因为它产生了不必要的承诺?即使我的应用程序运行正常,我是否应该花时间修复所有后端? (我的时间很短)或者差异会非常明显?谢谢你的帮助【参考方案2】:
解决方案是将我的具体操作放入另一个承诺中并使用 promise.all
return new Promise( (resolve,reject) =>
let data1;
let data2;
let promise1 = new Promise( (resolve,reject) =>
let url1 = "http://fakeapi.com/getData1";
fetch(url1)
.then(res => res.json())
.then(data => data1SpecificAction())
.then(data =>
data1 = data;
resolve()
.catch(()=>reject("cannot fetch data1"))
)
let promise 2 = new Promise( (resolve,reject) =>
let url2 = "http://fakeapi.com/getData2";
fetch(url2)
.then(res => res.json())
.then(data =>
data2 = data;
resolve()
)
.catch(()=>reject("cannot fetch data2"))
)
Promise.all([promise1,promise2])
.then(
() =>
if(data1 && data2)
resolve()
else
reject()
)
here 是一个解释如何在 Promise 之间传递数据的链接。
【讨论】:
@trincot 这个怎么样? 我不明白那篇文章如何适用于您的情况。那篇文章是关于如何在属于同一链的 Promise 链之间共享数据 - 你有两个独立的 Promise 链。我提供了一个答案,这是我能看到的最简化的方法,可以让它工作并避免各种反模式。 这不是一个好的解决方案,因为您一直在使用 Promise 构造函数反模式。以上是关于如何在 Promise 之间异步传递数据?的主要内容,如果未能解决你的问题,请参考以下文章