等待多个异步函数在 javascript 内完成
Waiting for multiple async functions to finish in javascript
我想向服务器发出一系列 ajax 请求,然后使用我之前收到的数据执行最终 ajax 请求。显然,我需要等待前面的请求完成才能执行最后一个请求。我在 javascript.
中实现时遇到问题
我不想让服务器不堪重负,所以理想情况下,所有请求都会按顺序完成。
我的简单测试代码如下(用sleep代替web请求):
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay));
var urls = ['1', '2', '3'];
const slowFunc = () => {
urls.forEach(async (url) => {
//Don't change this section!
console.log("a"+url);
await sleep(5000);
console.log("b"+url); //I want this to run before c
});
};
slowFunc();
console.log("c");
这在睡眠完成之前打印“c”,这是错误的。我怎样才能得到如下输出?
a1
b1
a2
b2
a3
b3
c
出于兴趣,我将如何获得此输出? (a 和 b 部分中的确切顺序并不重要。)
a1
a2
a3
b1
b2
b3
c
我尝试阅读 ES2018: asynchronous iteration 但它让我大吃一惊。
更新:我很快就重新考虑了我的例子,所以这是一个更好的例子(仍然不起作用):
var urls = ['https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.2.0/purify.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.8.3/system.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/slim-select/1.18.6/slimselect.min.js'];
var results = {};
const webRequest = (url) => {
$.ajax({
type: "GET",
url: url,
}).then(data => {
results[url] = data;
console.log("b"+url+","+results[url]); //I want this to run before c
});
}
const slowFunc = () => {
urls.forEach((url) => {
console.log("a"+url);
webRequest(url);
});
};
slowFunc();
console.log("c");
感谢到目前为止的评论。
更新 2:Web 请求问题的解决方案,基于 Antonio Della Fortuna 的建议:
var urls = ['https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.2.0/purify.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.8.3/system.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/slim-select/1.18.6/slimselect.min.js'];
var results = {};
const webRequest = (url) => {
return new Promise((resolve, reject) => {
$.ajax({
type: "GET",
url: url,
error: function (data, status, er) {
console.log("b,"+url+",failed");
resolve();
},
}).then(data => {
results[url] = data;
console.log("b,"+url+","+results[url]); //I want this to run before c
resolve();
});
});
}
const slowFunc = async () => {
for (let i = 0; i < urls.length; i++)
{
var url = urls[i];
console.log("a,"+url);
await webRequest(url);
};
};
slowFunc().then(() => {
console.log("c");
console.log(results);
})
在迭代时执行异步操作并不像您预期的那样有效。
当您执行 forEach 时,每个元素都将被同步迭代。因此,每个元素都将被迭代并调用回调函数,这就是为什么您首先看到每个元素的 'a' 日志。
例外情况是使用 for...of 循环,但对于其他迭代器,await 只会在回调函数内阻塞。
如果您试图将一段时间内的请求量限制为 API,您可以实施漏桶算法。或者,您可以使用延迟函数将迭代重构为 for...of 循环,以阻止保持顺序但不太理想的请求,因为请求的速度将是您的延迟时间加上完成其他异步任务的时间。
有两种方法取决于您的用例,您可以在此处找到工作示例 -> https://codesandbox.io/s/zen-carson-ksgzf?file=/src/index.js:569-624:
- 并行解决方案:您可以运行并行处理函数内部的请求,然后像这样打印
"c"
:
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay));
var urls = ["1", "2", "3"];
const slowFunc = async () => {
await Promise.all(
urls.map(async (url) => {
//Don't change this section!
console.log("a" + url);
await sleep(5000);
console.log("b" + url); //I want this to run before c
})
);
};
slowFunc().then(() => {
console.log("c");
});
- 同步解决方案:您可以运行所有请求就好像它们是同步的并按顺序等待:
const slowFuncSeq = async () => {
for (const url of urls) {
//Don't change this section!
console.log("a" + url);
await sleep(5000);
console.log("b" + url); //I want this to run before c
}
};
slowFuncSeq().then(() => {
console.log("c");
})
我想向服务器发出一系列 ajax 请求,然后使用我之前收到的数据执行最终 ajax 请求。显然,我需要等待前面的请求完成才能执行最后一个请求。我在 javascript.
中实现时遇到问题我不想让服务器不堪重负,所以理想情况下,所有请求都会按顺序完成。
我的简单测试代码如下(用sleep代替web请求):
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay));
var urls = ['1', '2', '3'];
const slowFunc = () => {
urls.forEach(async (url) => {
//Don't change this section!
console.log("a"+url);
await sleep(5000);
console.log("b"+url); //I want this to run before c
});
};
slowFunc();
console.log("c");
这在睡眠完成之前打印“c”,这是错误的。我怎样才能得到如下输出?
a1
b1
a2
b2
a3
b3
c
出于兴趣,我将如何获得此输出? (a 和 b 部分中的确切顺序并不重要。)
a1
a2
a3
b1
b2
b3
c
我尝试阅读 ES2018: asynchronous iteration 但它让我大吃一惊。
更新:我很快就重新考虑了我的例子,所以这是一个更好的例子(仍然不起作用):
var urls = ['https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.2.0/purify.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.8.3/system.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/slim-select/1.18.6/slimselect.min.js'];
var results = {};
const webRequest = (url) => {
$.ajax({
type: "GET",
url: url,
}).then(data => {
results[url] = data;
console.log("b"+url+","+results[url]); //I want this to run before c
});
}
const slowFunc = () => {
urls.forEach((url) => {
console.log("a"+url);
webRequest(url);
});
};
slowFunc();
console.log("c");
感谢到目前为止的评论。
更新 2:Web 请求问题的解决方案,基于 Antonio Della Fortuna 的建议:
var urls = ['https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.2.0/purify.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.8.3/system.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/slim-select/1.18.6/slimselect.min.js'];
var results = {};
const webRequest = (url) => {
return new Promise((resolve, reject) => {
$.ajax({
type: "GET",
url: url,
error: function (data, status, er) {
console.log("b,"+url+",failed");
resolve();
},
}).then(data => {
results[url] = data;
console.log("b,"+url+","+results[url]); //I want this to run before c
resolve();
});
});
}
const slowFunc = async () => {
for (let i = 0; i < urls.length; i++)
{
var url = urls[i];
console.log("a,"+url);
await webRequest(url);
};
};
slowFunc().then(() => {
console.log("c");
console.log(results);
})
在迭代时执行异步操作并不像您预期的那样有效。
当您执行 forEach 时,每个元素都将被同步迭代。因此,每个元素都将被迭代并调用回调函数,这就是为什么您首先看到每个元素的 'a' 日志。
例外情况是使用 for...of 循环,但对于其他迭代器,await 只会在回调函数内阻塞。
如果您试图将一段时间内的请求量限制为 API,您可以实施漏桶算法。或者,您可以使用延迟函数将迭代重构为 for...of 循环,以阻止保持顺序但不太理想的请求,因为请求的速度将是您的延迟时间加上完成其他异步任务的时间。
有两种方法取决于您的用例,您可以在此处找到工作示例 -> https://codesandbox.io/s/zen-carson-ksgzf?file=/src/index.js:569-624:
- 并行解决方案:您可以运行并行处理函数内部的请求,然后像这样打印
"c"
:
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay));
var urls = ["1", "2", "3"];
const slowFunc = async () => {
await Promise.all(
urls.map(async (url) => {
//Don't change this section!
console.log("a" + url);
await sleep(5000);
console.log("b" + url); //I want this to run before c
})
);
};
slowFunc().then(() => {
console.log("c");
});
- 同步解决方案:您可以运行所有请求就好像它们是同步的并按顺序等待:
const slowFuncSeq = async () => {
for (const url of urls) {
//Don't change this section!
console.log("a" + url);
await sleep(5000);
console.log("b" + url); //I want this to run before c
}
};
slowFuncSeq().then(() => {
console.log("c");
})