问题描述
我正在从API中获取某些人员,然后并行执行它们,如果我串行执行代码,代码将如何显示?甚至不确定,如果下面是并行的,我很难弄清它们两者之间的区别.
I am fetching some persons from an API and then executing them in parallel, how would the code look if i did it in serial?Not even sure, if below is in parallel, i'm having a hard time figuring out the difference between the two of them.
我猜serial是一个接一个的,而parallel(promise.all)正在等待所有promise解析,然后才将值放入finalResult?
I guess serial is one by one, and parallel (promise.all) is waiting for all promises to be resolved before it puts the value in finalResult?
这理解正确吗?
下面是我的代码段.
谢谢.
const fetch = require('node-fetch')
const URL = "https://swapi.co/api/people/";
async function fetchPerson(url){
const result = await fetch(url);
const data = await result.json().then((data)=>data);
return data ;
}
async function printNames() {
const person1 = await fetchPerson(URL+1);
const person2 = await fetchPerson(URL+2);
let finalResult =await Promise.all([person1.name, person2.name]);
console.log(finalResult);
}
printNames().catch((e)=>{
console.log('There was an error :', e)
});
推荐答案
让我将此代码翻译成几个不同的版本.首先,是原始版本,但删除了其他工作:
Let me translate this code into a few different versions. First, the original version, but with extra work removed:
const fetch = require('node-fetch')
const URL = "https://swapi.co/api/people/";
async function fetchPerson(url){
const result = await fetch(url);
return await result.json();
}
async function printNames() {
const person1 = await fetchPerson(URL+1);
const person2 = await fetchPerson(URL+2);
console.log([person1.name, person2.name]);
}
try {
await printNames();
} catch(error) {
console.error(error);
}
上面的代码等同于您发布的原始代码.现在,为了更好地了解此处发生的情况,让我们将其翻译为与async
/await
完全相同的代码.
The code above is equivalent to the original code you posted. Now to get a better understanding of what's going on here, let's translate this to the exact same code pre-async
/await
.
const fetch = require('node-fetch')
const URL = "https://swapi.co/api/people/";
function fetchPerson(url){
return fetch(url).then((result) => {
return result.json();
});
}
function printNames() {
let results = [];
return fetchPerson(URL+1).then((person1) => {
results.push(person1.name);
return fetchPerson(URL+2);
}).then((person2) => {
results.push(person2.name);
console.log(results);
});
}
printNames().catch((error) => {
console.error(error);
});
上面的代码等同于您发布的原始代码,我只是做了JS转换器要做的额外工作.我觉得这使它更加清晰.按照上面的代码,我们将执行以下操作:
The code above is equivalent to the original code you posted, I just did the extra work the JS translator will do. I feel like this makes it a little more clear. Following the code above, we will do the following:
- 致电
printNames()
-
printNames()
将请求person1
并等待响应. -
printNames()
将请求person2
并等待响应. -
printNames()
将打印结果
- Call
printNames()
printNames()
will requestperson1
and wait for the response.printNames()
will requestperson2
and wait for the response.printNames()
will print the results
您可以想象,这可以得到改善.让我们同时请求两个人.我们可以使用以下代码做到这一点
As you can imagine, this can be improved. Let's request both persons at the same time. We can do that with the following code
const fetch = require('node-fetch')
const URL = "https://swapi.co/api/people/";
function fetchPerson(url){
return fetch(url).then((result) => {
return result.json();
});
}
function printNames() {
return Promise.all([fetchPerson(URL+1).then((person1) => person1.name), fetchPerson(URL+2).then((person2) => person2.name)]).then((results) => {
console.log(results);
});
}
printNames().catch((error) => {
console.error(error);
});
此代码与发布的原始代码不同.现在,我们不再并行执行所有操作,而是并行获取不同的用户.现在我们的代码执行以下操作
This code is NOT equivalent to the original code posted. Instead of performing everything serially, we are now fetching the different users in parallel. Now our code does the following
- 致电
printNames()
-
printNames()
将- 发送对
person1
的请求 - 发送请求
person2
- 发送对
- Call
printNames()
printNames()
will- Send a request for
person1
- Send a request for
person2
- Send a request for
故事的寓意是async
/await
并不能在所有情况下替代Promises,它是语法糖,使处理Promises的特定方式变得更加容易.如果您希望/可以并行执行任务,请不要在每个Promise上都使用async
/await
.您可以改为执行以下操作:
The moral of the story is that async
/await
is not a replacement for Promises in all situations, it is syntactic sugar to make a very specific way of handling Promises easier. If you want/can perform tasks in parallel, don't use async
/await
on each individual Promise. You can instead do something like the following:
const fetch = require('node-fetch')
const URL = "https://swapi.co/api/people/";
async function fetchPerson(url){
const result = await fetch(url);
return result.json();
}
async function printNames() {
const [ person1, person2 ] = await Promise.all([
fetchPerson(URL+1),
fetchPerson(URL+2),
]);
console.log([person1.name, person2.name]);
}
printNames().catch((error) => {
console.error(error);
});
免责声明
printNames()
(以及其他所有内容)实际上并没有等待.它将继续执行I/O之后未出现在I/O回调中的所有代码. await
仅创建I/O完成时调用的其余代码的回调.例如,以下代码片段将无法满足您的期望.
printNames()
(and everything else) doesn't really wait for anything. It will continue executing any and all code that comes after the I/O that does not appear in a callback for the I/O. await
simply creates a callback of the remaining code that is called when the I/O has finished. For example, the following code snippet will not do what you expect.
const results = Promise.all([promise1, promise2]);
console.log(results); // Gets executed immediately, so the Promise returned by Promise.all() is printed, not the results.
串行与并行
关于在评论中与OP的讨论,我还想添加对串行工作与并行工作的描述.我不确定您对这些不同概念的熟悉程度,所以我将对它们进行漂亮的抽象描述.
Serial vs. Parallel
In regards to my discussion with the OP in the comments, I wanted to also add a description of serial vs. parallel work. I'm not sure how familiar you are with the different concepts, so I'll give a pretty abstraction description of them.
首先,我很谨慎地说JS在同一JS环境中不支持并行操作.具体来说,与其他我可以启动线程以并行执行任何工作的语言不同,JS只有在其他事情正在执行(I/O)时,JS才(似乎)可以并行执行工作.
First, I find it prudent to say that JS does not support parallel operations within the same JS environment. Specifically, unlike other languages where I can spin up a thread to perform any work in parallel, JS can only (appear to) perform work in parallel if something else is doing the work (I/O).
话虽如此,让我们从串行与并行外观的简单描述开始.想象一下,如果愿意,您可以为4个不同的班级做作业.下表显示了每个班级完成家庭作业的时间.
That being said, let's start off with a simple description of what serial vs. parallel looks like. Imagine, if you will, doing homework for 4 different classes. The time it takes to do each class's homework can be seen in the table below.
Class | Time
1 | 5
2 | 10
3 | 15
4 | 2
自然,您所做的工作将依次进行,看起来像
Naturally, the work you do will happen serially and look something like
You_Instance1: doClass1Homework() -> doClass2Homework() -> doClass3Homework() -> doClass4Homework()
连续执行作业需要32个时间.但是,如果您可以将自己分为4个不同的实例,那不是很好吗?在这种情况下,您可以为每个类都拥有一个自己的实例.可能看起来像
Doing the homework serially would take 32 units of time. However, wouldn't be great if you could split yourself into 4 different instances of yourself? If that were the case, you could have an instance of yourself for each of your classes. This might look something like
You_Instance1: doClass1Homework()
You_Instance2: doClass2Homework()
You_Instance3: doClass3Homework()
You_Instance4: doClass4Homework()
并行工作,您现在可以在15个单位时间内完成作业!不到一半的时间.
Working in parallel, you can now finish your homework in 15 units of time! That's less than half the time.
但是等等,"您说,将自己拆分为多个实例来做我的作业有一定的缺点,否则每个人都会做."
"But wait," you say, "there has to be some disadvantage to splitting myself into multiple instances to do my homework or everybody would be doing it."
您是正确的.将您自己分为多个实例会产生一些开销.假设您要分手,需要进行深度冥想和身体体验,这需要5个时间单位.现在完成作业看起来像:
You are correct. There is some overhead to splitting yourself into multiple instances. Let's say that splitting yourself requires deep meditation and an out of body experience, which takes 5 units of time. Now finishing your homework would look something like:
You_Instance1: split() -> split() -> doClass1Homework()
You_Instance2: split() -> doClass2Homework()
You_Instance3: doClass3Homework()
You_Instance4: doClass4Homework()
现在,完成作业需要25个时间,而不是花费15个时间.这仍然比自己完成所有作业便宜.
Now instead of taking 15 units of time, completing your homework takes 25 units of time. This is still cheaper than doing all of your homework by yourself.
摘要(如果您了解串行和并行执行,请在此处跳过)
Summary (Skip here if you understand serial vs. parallel execution)
这可能是一个愚蠢的例子,但这正是串行与并行执行的样子.并行执行的主要优点是可以同时执行多个长时间运行的任务.由于多个工人同时在做某事,因此工作可以更快地完成.
This may be a silly example, but this is exactly what serial vs. parallel execution looks like. The main advantage of parallel execution is that you can perform several long running tasks at the same time. Since multiple workers are doing something at the same time, the work gets done faster.
但是,有一些缺点.最大的两个是开销和复杂性.无论您使用哪种环境/语言,并行执行代码都是免费的.在JS中,并行执行可能会非常昂贵,因为这是通过将请求发送到服务器来实现的.根据各种因素,往返可能需要10到100毫秒的时间.对于现代计算机而言,这非常慢.这就是为什么并行执行通常保留给长时间运行的进程(完成作业)或无法避免(从磁盘或服务器加载数据)的原因.
However, there are disadvantages. Two of the big ones are overhead and complexity. Executing code in parallel isn't free, no matter what environment/language you use. In JS, parallel execution can be quite expensive because this is achieved by sending a request to a server. Depending on various factors, the round trip can take 10s to 100s of milliseconds. That is extremely slow for modern computers. This is why parallel execution is usually reserved for long running processes (completing homework) or when it just cannot be avoided (loading data from disk or a server).
另一个主要缺点是增加了复杂性.协调并行执行的多个任务可能很困难(餐厅哲学家,消费者与生产者问题,饥饿,种族条件).但是在JS中,复杂性还来自对代码的理解(回调地狱,了解何时执行什么).如上所述,在异步代码之后出现的一组指令不会等待执行,直到异步代码完成(除非它发生在回调中).
The other main disadvantage is the added complexity. Coordinating multiple tasks occurring in parallel can be difficult (Dining Philosophers, Consumer-Producer Problem, Starvation, Race Conditions). But in JS the complexity also comes from understanding the code (Callback Hell, understanding what gets executed when). As mentioned above, a set of instructions occurring after asynchronous code does not wait to execute until the asynchronous code completes (unless it occurs in a callback).
我如何获得自己的多个实例来用JS做作业?
有两种不同的方法可以完成此操作.一种方法是设置4个不同的服务器.我们称它们为class1Server
,class2Server
,class3Server
和class4Server
.现在,要使这些服务器并行执行作业,您将需要执行以下操作:
There are a couple of different ways to accomplish this. One way you could do this is by setting up 4 different servers. Let's call them class1Server
, class2Server
, class3Server
, and class4Server
. Now to make these servers do your homework in parallel, you would do something like this:
Promise.all([
startServer1(),
startServer2(),
startServer3(),
startServer4()
]).then(() => {
console.log("Homework done!");
}).catch(() => {
console.error("One of me had trouble completing my homework :(");
});
Promise.all()
返回一个Promise,它会在所有Promise都解决后解决,或者在其中一个被拒绝时拒绝.
Promise.all()
returns a Promise that either resolves when all of the Promises are resolved or rejects when one of them is rejected.
这篇关于串行和并行之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!