随笔:Node流程控制

Node的流程控制

什么是流程控制

在Node中,流程控制是让一组异步任务顺序执行的概念,流程控制分为串行并行
串行示意图:

Start
work-1
work-2
...
work-n
Stop

并行示意图:

Start
work-1
Stop
work-2
...
work-n

串行流程控制

串行的应用场景

串行流程控制是在某些情况下,相关操作必须按顺序执行而产生的一种控制方式。比如我要在一个新目录test里创建一个新文件test1.txt,并且在test1.txt中写入hello world,然后再创建一个新文件test2.js读取test1.txt中的hello word一样,这里的顺序只能有一个,尽管文件的读写是异步,但是我们只能等待上一步完成,才能继续进行下一步。

创建目录test
创建新文件test1.txt
在test1.txt中写入helloword
创建新文件test2.js
写下读取test1.txt的代码

实现串行的方法

  • 使用定时器setTimeout来模拟
    这里使用三个定时器来模拟,当第一个定时器回调函数执行后会将第二个定时器挂起等待回调执行,然后执行第二个回调,挂起第三个定时器等待回调执行,最后执行第三个回调,依次输出:
first
next
last
setTimeout(() => {
    console.log('I execute first.');
    setTimeout(() => {
        console.log('I execute next.');
        setTimeout(() => {
            console.log('I execute last.');
        }, 100);
    }, 500);
}, 1000);
  • 使用async库实现
    利用第三方开源库async可以轻松实现顺序执行,只需要把顺序执行的函数放入数组中即可。
    使用前先安装async
npm i --save async
const async = require('async');
async.series([
  callback => {
    setTimeout(() => {
      console.log('I execute first.');
      callback();
    }, 1000);
  },
  callback => {
    setTimeout(() => {
      console.log('I execute next.');
      callback();
    }, 500);
  },
  callback => {
    setTimeout(() => {
      console.log('I execute last.');
      callback();
    }, 100);
  }
]);
  • 手动实现
    串行化流程控制本质上是在需要时让回调进场,而不是简单地把它们嵌套起来。这里我们定义一个tasks,用它来保存每一个异步函数的操作,通过next()来依次调用,并将结果作为参数传入下一个函数中去,从而保证执行的顺序。
const fs = require('fs');
const request = require('request');
const htmlparser = require('htmlparser');
const configFilename = './rss_feeds.txt';

function checkForRSSFile() {
  fs.exists(configFilename, (exists) => {
    if (!exists)
      return next(new Error(`Missing RSS file: ${configFilename}`));
    next(null, configFilename);
  });
}

function readRSSFile(configFilename) {
  fs.readFile(configFilename, (err, feedList) => {
    if (err) return next(err);
    feedList = feedList
      .toString()
      .replace(/^\s+|\s+$/g, '')
      .split('\n');
    const random = Math.floor(Math.random() * feedList.length);
    next(null, feedList[random]);
  });
}

function downloadRSSFeed(feedUrl) {
  request({
    uri: feedUrl
  }, (err, res, body) => {
    if (err) return next(err);
    if (res.statusCode !== 200) return next(new Error('Abnormal response status code'));
    next(null, body);
  });
}

function parseRSSFeed(rss) {
  const handler = new htmlparser.RssHandler();
  const parser = new htmlparser.Parser(handler);
  parser.parseComplete(rss);
  if (!handler.dom.items.length)
    return next(new Error('No RSS items found'));
  const item = handler.dom.items.shift();
  console.log(item.title);
  console.log(item.link);
}
const tasks = [
  checkForRSSFile,
  readRSSFile,
  downloadRSSFeed,
  parseRSSFeed
];

function next(err, result) {
  if (err) throw err;
  const currentTask = tasks.shift();
  if (currentTask) {
    currentTask(result);
  }
}
next();

并行流程控制

并行的应用场景

同时请求多个资源、读取多个文件

实现并行的方法

为了让异步任务并行执行,仍然是要把任务放到数组中,但任务的存放顺序无关紧要。每个任务都应该调用处理器函数增加已完成任务的计数值。当所有任务都完成后,处理器函数应该执行后续的逻辑。
用并行化流程控制实现对几个文件中单词频度的计数:

得到目录中的文件列表
读取文件1
显示单词的计数值
读取文件2
...
读取文件n

手动实现并行

const fs = require('fs');
const tasks = [];
const wordCounts = {};
const filesDir = './text';
let completedTasks = 0;

function checkIfComplete() {
  completedTasks++;
  if (completedTasks === tasks.length) {
    for (let index in wordCounts) {
      console.log(`${index}: ${wordCounts[index]}`);
    }
  }
}

function addWordCount(word) {
  wordCounts[word] = (wordCounts[word]) ? wordCounts[word] + 1 : 1;
}

function countWordsInText(text) {
  const words = text
    .toString()
    .toLowerCase()
    .split(/\W+/)
    .sort();
  words
    .filter(word => word)
    .forEach(word => addWordCount(word));
}

fs.readdir(filesDir, (err, files) => {
  if (err) throw err;
  files.forEach(file => {
    const task = (file => {
      return () => {
        fs.readFile(file, (err, text) => {
          if (err) throw err;
          countWordsInText(text);
          checkIfComplete();
        });
      };
    })(`${filesDir}/${file}`);
    tasks.push(task);
  })
  tasks.forEach(task => task());
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值