【问题标题】:Difference between Serial and parallel串行和并行之间的区别
【发布时间】:2018-11-13 18:11:28
【问题描述】:

我从 API 中获取一些人,然后并行执行它们,如果我以串行方式执行,代码会如何? 甚至不确定,如果下面是并行的,我很难弄清楚它们之间的区别。

我猜serial是一一对应的,parallel (promise.all) 是在等所有promise都解决后才把值放到finalResult中?

这样理解正确吗?

下面是我的代码的 sn-p。

提前致谢。

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)

      });

【问题讨论】:

  • 你熟悉 JS 中的 Promise 和异步代码吗?回答该问题将使答案与您想要的更相关。
  • 我发布了对您的问题的详细答案,但我不确定您是否想理解。如果没有,请告诉我,我可以尝试更新它。
  • @c1moore 您的回答真的很有帮助,很多部分都汇集在一起​​了:) 我想了解的是串行和并行之间的确切区别是什么?优点和缺点。我何时以及如何使用它们中的任何一个?我一直试图了解串行与并行,但很难找到以清晰方式概述差异的示例。其中大多数仅适用于作者 e-peen。因此,我们将不胜感激。
  • 我不确定 JS 是探索串行与并行代码的最佳语言。 JS 比其他语言更受限制;但是,当我有时间时,我也会更新我的答案以解决其中一些主题。如果我在一天结束之前没有回到它,请随时 ping 我以提醒我这样做。
  • 嗯,这是我必须使用 JS 的考试,所以没有办法解决它。甚至不确定我的老师能否正确解释。慢慢来,我的朋友,你的帮助最大。

标签: javascript asynchronous async-await


【解决方案1】:

让我把这段代码翻译成几个不同的版本。首先是原始版本,但删除了额外的工作:

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);
}

上面的代码等同于您发布的原始代码。现在为了更好地理解这里发生了什么,让我们把它翻译成完全相同的代码 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翻译器会做的额外工作。我觉得这让它更清楚一点。按照上面的代码,我们将执行以下操作:

  1. 致电printNames()
  2. printNames() 将请求 person1 并等待响应。
  3. printNames() 将请求 person2 并等待响应。
  4. printNames() 将打印结果

您可以想象,这可以改进。让我们同时请求两个人。我们可以用下面的代码做到这一点

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);
});

此代码不等同于发布的原始代码。我们现在不是串行执行所有操作,而是并行获取不同的用户。现在我们的代码执行以下操作

  1. 致电printNames()
  2. printNames()
    • 发送person1的请求
    • 发送person2的请求
  3. printNames() 将等待两个请求的响应
  4. printNames() 将打印结果

这个故事的寓意是async/await 并不是在所有情况下都可以替代 Promise,它是一种语法糖,可以使处理 Promises 的非常具体的方式变得更容易。如果您希望/可以并行执行任务,请不要在每个单独的 Promise 上使用 async/await。您可以改为执行以下操作:

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 完成时调用该回调。例如,下面的代码 sn -p 不会做你所期望的。

const results = Promise.all([promise1, promise2]);

console.log(results);  // Gets executed immediately, so the Promise returned by Promise.all() is printed, not the results.

串行与并行

关于我在 cmets 中与 OP 的讨论,我还想添加对串行与并行工作的描述。我不确定你对不同的概念有多熟悉,所以我会给出一个相当抽象的描述。

首先,我认为 JS 不支持并行操作在同一个 JS 环境中是谨慎的。具体来说,与我可以启动一个线程以并行执行任何工作的其他语言不同,JS 只能(似乎)在其他事物正在执行工作(I/O)时并行执行工作。

话虽如此,让我们从串行与并行外观的简单描述开始。想象一下,如果你愿意,为 4 个不同的班级做作业。完成每个班级的家庭作业所需的时间如下表所示。

Class | Time
1     | 5
2     | 10
3     | 15
4     | 2

当然,你所做的工作会连续发生,看起来像

You_Instance1: doClass1Homework() -> doClass2Homework() -> doClass3Homework() -> doClass4Homework()

连续完成作业需要 32 个单位的时间。但是,如果您可以将自己分成 4 个不同的实例,那不是很好吗?如果是这样的话,你可以为你的每个类都有一个你自己的实例。这可能看起来像

You_Instance1: doClass1Homework()
You_Instance2: doClass2Homework()
You_Instance3: doClass3Homework()
You_Instance4: doClass4Homework()

并行工作,您现在可以在 15 个单位时间内完成作业!这不到一半的时间。

“但是等等,”你说,“将自己分成多个实例来完成我的作业肯定有一些缺点,否则每个人都会这样做。”

你是对的。将自己拆分为多个实例会产生一些开销。假设分裂自己需要深度冥想和出体体验,这需要 5 个单位的时间。现在完成你的作业看起来像这样:

You_Instance1: split() -> split() -> doClass1Homework()
You_Instance2:            split() -> doClass2Homework()
You_Instance3:                       doClass3Homework()
You_Instance4:                       doClass4Homework()

现在完成作业需要 25 个单位,而不是 15 个单位的时间。这仍然比自己完成所有作业要便宜。

总结(如果您了解串行与并行执行,请跳过此处)

这可能是一个愚蠢的例子,但这正是串行与并行执行的样子。并行执行的主要优点是您可以同时执行多个长时间运行的任务。由于多名工作人员同时在做某事,因此工作完成得更快。

但是,也有缺点。其中最大的两个是开销和复杂性。无论您使用什么环境/语言,并行执行代码都不是免费的。在 JS 中,并行执行可能非常昂贵,因为这是通过向服务器发送请求来实现的。根据各种因素,往返可能需要 10 到 100 毫秒。这对于现代计算机来说非常慢。这就是为什么通常为长时间运行的进程(完成作业)或无法避免的情况(从磁盘或服务器加载数据)保留并行执行。

另一个主要缺点是增加了复杂性。协调并行发生的多个任务可能很困难(Dining PhilosophersConsumer-Producer ProblemStarvationRace Conditions)。但是在 JS 中,复杂性也来自对代码的理解(Callback Hell,理解什么时候执行)。如上所述,在异步代码之后发生的一组指令不会等到异步代码完成后才执行(除非它发生在回调中)。

如何让我自己的多个实例在 JS 中完成作业?

有几种不同的方法可以做到这一点。一种方法是设置 4 个不同的服务器。我们称它们为class1Serverclass2Serverclass3Serverclass4Server。现在要让这些服务器并行完成您的作业,您可以执行以下操作:

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 都解决时解决,要么在其中一个被拒绝时拒绝。

【讨论】:

  • 顺便说一下,上面的例子,你自己拆分,实际上可以在 20 个单位时间内执行。我将此作为练习留给阅读此评论的任何人。
【解决方案2】:

fetchPersonprintNames 两个函数都以串行方式运行,就像您在 awaiting 结果一样。在您的情况下,Promise.all 的使用毫无意义,因为这两个人都已经是awaited(已解决)。

并行获取两个人:

const [p1, p2] = await Promise.all([fetchPerson(URL + '1'), fetchPerson(URL + '2')])

给定两个异步函数:

const foo = async () => {...}
const bar = async () => {...}

这是连续剧:

const x = await foo()
const y = await bar()

这是并行的:

const [x,y] = await Promise.all([foo(), bar()])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-08
    • 2015-10-24
    • 2012-12-17
    • 1970-01-01
    • 1970-01-01
    • 2021-04-23
    • 2015-11-18
    • 2018-06-02
    相关资源
    最近更新 更多