串行和并行

时间:2018-06-04 11:56:22

标签: javascript asynchronous async-await

我从一个API中获取一些人然后并行执行它们,如果我连续执行它,代码会怎么样? 甚至不确定,如果下面是平行的,我很难搞清楚他们两者之间的区别。

我猜串口是一个接一个,并行(promise.all)等待所有的promises在将它放入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)

      });

2 个答案:

答案 0 :(得分:2)

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

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之前将其转换为完全相同的代码。

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()将打印结果
  5. 可以想象,这可以改进。让我们同时请求。我们可以使用以下代码

    来实现
    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()将打印结果
    5. 故事的寓意是async / await并不是所有情况下Promise的替代品,因此使一个非常具体的处理Promise的方法更容易。如果您希望/可以并行执行任务,请不要使用async / await

      <强>声明

      printNames()(以及其他所有内容)并非等待任何事情。它将继续执行I / O之后出现的任何和所有代码,这些代码不会出现在I / O的回调中。 await只是创建了在I / O完成时调用的剩余代码的回调。例如,以下代码段将无法满足您的期望。

      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的讨论,我还想添加串行与并行工作的描述。我不确定你对不同的概念有多熟悉,所以我会给出一个非常抽象的描述。

      首先,我认为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个单位的时间内完成作业!那不到一半的时间。

      &#34;但等等,&#34;你说,&#34;将自己分成多个实例来完成我的家庭作业或者每个人都会这样做是不利的。&#34;

      你是对的。将自己分成多个实例会有一些开销。让我们说分裂自己需要深度冥想和身体体验,这需要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被解析时解析,也可以在其中一个Promise被拒绝时拒绝。

答案 1 :(得分:-1)

fetchPersonprintNames这两个函数都是串行运行的,因为await结果。在您的情况下Promise.all使用毫无意义,因为这两个人已经await编辑(已解决)。

同时取两个人:

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()])