所以我一直在尝试一个可能的解决方案,但我尝试截取一个简单的例子却没有成功。
所以我有一个数据数组,看起来像这样:
const arr = [{
"content": "FIRST_CONTENT_HERE",
"callback": function callback () {}
},
{
"content": "SECOND_CONTENT_HERE",
"callback": function callback () {}
},
{
"content": "THIRD_CONTENT_HERE",
"callback": function callback () {}
},
{
"content": "THIRD_CONTENT_HERE",
"callback": function callback () {}
}
];
我想创建一个生成器来迭代该数组,但在执行回调函数之前,不会将(yield?)控制返回到下一次迭代。我尝试将该回调封装在promise中,并使其在执行该回调之前不会解析promise。我试着确保回调是一个异步函数来包装承诺。我尝试了一些东西,但就是没能让它们发挥作用。在我调用生成器函数并对其进行迭代之后,它只是遍历了数组的每个元素。无论我如何设置循环,它都不会在进入下一次迭代之前等待回调执行。
我四处搜索了生成器以及它们如何与promise(或异步函数)交互的大多数示例,这些示例只显示了类似yield Promise.resolve(x)
的内容,这不是我想要的。我不希望它立即解决——我需要它等待callback
执行。回调将被设置为onclick处理程序(关闭对话框),并且只有在该事件发生时才会执行。因此,基本上,下一个对话框不应该实例化,直到上一个对话框关闭。因此,对于数组的每个循环,都会发生一些事情:
- 实例化对话框
- 为该对话框设置onclick处理程序。最终,
callback()
将在其中的某个地方执行 - 我可以将回调封装在一个函数中,该函数返回一个Promise(或类似的东西),在该函数中,Promise直到事件触发才解析。因此,当它启动时,它既解析promise,又执行回调
- 当这种情况发生时,迭代器/生成器继续进行下一次迭代并实例化下一个对话框。然后是下一个,直到原始数组循环
我并不是说我的方法是正确的。我可能完全偏离了底线。但我的脑海里有一个小问题,那就是发电机和Promises是我要走的路。
我将如何最好地实现我试图实现的功能?正如我所说,我就是无法理解。。。
下面是我编写的一些代码的示例。当我尝试不同的东西时,这已经经历了几个变化。我在这里使用fetch(),因为我知道它返回一个Promise。我确信Promises需要在我的解决方案中的某个地方发挥作用,以确保在控制继续之前执行我的非异步callback()
。这很模糊,但我只是在尝试不同的概念证明。
async function runRequest(url) {
const response = await fetch(url);
return response;
}
async function* setupGenerator(arr) {
for (let i = 0; i < arr.length; i++) {
const response = await runRequest(arr[i]);
yield response;
}
}
const urls = [
'/activityViewer/index.html',
'/message/index.html',
'/users/index.html',
];
debugger;
const generator = setupGenerator(urls);
for (let response of generator) {
console.log('In FOR loop; response; ', response);
}
thnx,
Christoph
您可以让回调成为打开对话框并返回promise的函数。这个承诺应该在调用点击处理程序时解决。
这里有一个例子:
promiseDialog
就是这样一个回调函数:它需要一个标题来显示,对话框允许用户输入一些输入并关闭对话框。
您可以让一个驱动程序函数迭代您给定的数组,但每次都使用promiseDialog
作为回调,并产生此回调返回的承诺。
最后,您可以使用for await
循环来消费这些已产生的承诺:
(注意:该对话框实际上不是模式对话框,但与演示无关)
function dialogPromise(title) {
const dialog = document.querySelector("#dialog");
dialog.querySelector("p").textContent = title;
dialog.style.display = "inline-block";
const button = dialog.querySelector("button");
const input = dialog.querySelector("input");
input.value = "";
input.focus();
return new Promise(function(resolve) {
button.addEventListener("click", function (e) {
e.preventDefault();
dialog.style.display = "none";
// Resolve when paint cycle has happened (to hide dialog)
requestAnimationFrame(() => requestAnimationFrame(() =>
resolve(input.value)
));
}, { once: true });
});
}
async function* chainDialogs(tasks) {
for (const task of tasks) {
yield task.callback(task.content);
}
}
(async function() {
const arr = [
{ "content": "FIRST_CONTENT_HERE", "callback": dialogPromise },
{ "content": "SECOND_CONTENT_HERE", "callback": dialogPromise },
{ "content": "THIRD_CONTENT_HERE", "callback": dialogPromise },
{ "content": "FOUR_CONTENT_HERE", "callback": dialogPromise }
];
for await (const result of chainDialogs(arr)) {
console.log(result);
}
})();
#dialog { position: absolute; display: none; margin: 5vw ; padding: 5px; border: 1px solid; background: #ee8 }
<form id="dialog">
<p>Test dialog</p>
Your answer: <input>
<button>OK</button>
</form>
您可以使用异步迭代:
const arr = [{
"content": "FIRST_CONTENT_HERE",
"callback": function callback (_) { return _; }
},
{
"content": "SECOND_CONTENT_HERE",
"callback": function callback (_) { return _; }
},
{
"content": "THIRD_CONTENT_HERE",
"callback": function callback (_) { return Promise.resolve(_); }
},
{
"content": "FOUR_CONTENT_HERE",
"callback": function callback (_) { return _; }
}
];
async function* asyncGenerator(tasks) {
for (const task of tasks) {
yield task.callback(task.content);
}
}
;(async function() {
for await (const txt of asyncGenerator(arr)) {
console.log('->', txt);
}
})();
生成器和承诺有不同的目标
我想创建一个生成器来迭代该数组,但在执行回调函数之前,不会将控制权返回(yield?)到下一次迭代
问题是生成器和Promises有根本不同的假设。承诺本质上是";我会一直工作到完成,完成后再给你打电话;而发电机基本上是"发电机";让我知道你什么时候准备好下一个"。
如果您使用生成器,则下次调用函数时必须返回一个值。。。你等不及了。所以问题是,等到第一次通话结束后再开始第二次通话对你来说有多重要?
如果您可以同时处理所有调用,那么您的生成器可以只返回Promises,并且您的调用代码可以负责await
调用它们。
但是,如果按顺序一次处理一个调用很重要,则不能使用生成器。但是,您可以使用回调。
解决方案1:使用回调参数来维护控制
这可能不是你所希望的,但如果你必须确保调用一次一个,那么这是一个简单的解决方案。
const arr = [{
"content": "FIRST_CONTENT_HERE",
"callback": function callback () {}
},
{
"content": "SECOND_CONTENT_HERE",
"callback": function callback () {}
},
{
"content": "THIRD_CONTENT_HERE",
"callback": function callback () {}
},
{
"content": "THIRD_CONTENT_HERE",
"callback": function callback () {}
}
];
async function processList(itemCallback) {
for (const item of arr) {
await item.callback();
itemCallback(item);
}
}
async function callingCode() {
await processList(item => {
// Do something with item...
});
}
解决方案2:通过返回Promises让调用者拥有控制权
如果生成器更注重便利性和语法美观,而不是性能或技术要求,那么您可以返回Promises并让调用代码处理它们:
const arr = [{
"content": "FIRST_CONTENT_HERE",
"callback": async function callback () {}
},
{
"content": "SECOND_CONTENT_HERE",
"callback": async function callback () {}
},
{
"content": "THIRD_CONTENT_HERE",
"callback": async function callback () {}
},
{
"content": "THIRD_CONTENT_HERE",
"callback": async function callback () {}
}
];
async function* getList() {
for (const item of arr) {
yield item.callback().then(() => item);
}
}
async function callingCode() {
for await (const item of getList()) {
// Do something with item
}
}