Node.js 异步流控制[通俗易懂]

Node.js 异步流控制[通俗易懂]在其核心,JavaScript被设计为在“主”线程上是非阻塞的,这是呈现视图的位置。你可以想象这在浏览器中的重要性。例如,当主线程被阻塞时,会导

欢迎大家来到IT世界,在知识的湖畔探索吧!

在其核心,JavaScript被设计为在“主”线程上是非阻塞的,这是呈现视图的位置。你可以想象这在浏览器中的重要性。例如,当主线程被阻塞时,会导致最终用户害怕的臭名昭著的“冻结”,并且无法调度其他事件,最终,导致数据丢失。

这就产生了一些只有函数式编程才能解决的独特约束。然而,在更复杂的过程中,回调可能会变得很难处理。这通常会导致“回调地狱”,其中带有回调的多个嵌套函数使代码在读取、调试、组织等方面更具挑战性。

例如:

async1(function (input, result1) {
	async2(function (result2) {
		async3(function (result3) {
			async4(function (result4) {
				async5(function (output) {
						// do something with output
				});
			});
		});
	});
});

欢迎大家来到IT世界,在知识的湖畔探索吧!

当然,在现实生活中,很可能会有额外的代码行来处理result1、result2等,因此,这个问题的长度和复杂性通常会导致代码看起来比上面的例子混乱得多。

这就是函数的用武之地。更复杂的操作由许多功能组成:

  1. 调用方式 input
  2. 中间件
  3. 终止器

“调用方式 input”是对列中的第一个函数。此功能将接受操作的原始输入(如果有)。操作是一系列可执行的功能,原始输入主要是:

  1. 全局环境中的变量
  2. 带参数或不带参数的直接调用
  3. 通过文件系统或网络请求获得的值

网络请求可以是由外部网络、同一网络上的另一应用程序或同一网络或外部网络上的应用程序本身发起的传入请求。

中间件函数将返回另一个函数终止器函数将调用回调。以下说明了网络或文件系统请求的流程。这里的延迟是0,因为所有这些值都在内存中可用。

欢迎大家来到IT世界,在知识的湖畔探索吧!function final(someInput, callback) {
	callback(`${someInput} and terminated by executing callback `);
}
function middleware(someInput, callback) {
	return final(`${someInput} touched by middleware `, callback);
}
function initiate() {
const someInput = 'hello this is a function ';
  middleware(someInput, function (result) {
  	console.log(result);
  	// requires callback to `return` result
  });
}
initiate();

状态管理

函数可能与状态相关,也可能不与状态相关。当函数的输入或其他变量依赖于外部函数时,就会产生状态依赖性。

通过这种方式,有两种主要的状态管理策略:

  1. 将变量直接传递给函数
  2. 从缓存、会话、文件、数据库、网络或其他外部源获取变量值。

注意,我没有提到全局变量。用全局变量管理状态通常是一种草率的反模式,这使得很难或不可能保证状态。在可能的情况下,应避免使用复杂程序中的全局变量。

控制流

如果一个对象在内存中可用,则可以进行迭代,并且不会对控制流进行更改:

function getSong() {
  let _song = '';
  let i = 100;
  for (i; i > 0; i -= 1) {
 	 _song += `${i} beers on the wall, you take one down and pass it around, ${
  		i - 1
  	} bottles of beer on the wall\n`;
  	if (i === 1) {
 			 _song += "Hey let's get some more beer";
  	}
  }
  return _song;
}
function singSong(_song) {
	if (!_song) throw new Error("song is '' empty, FEED ME A SONG!");
	console.log(_song);
}
const song = getSong();
// this will work
singSong(song);

但是,如果数据在内存中不存在,则迭代将停止:

欢迎大家来到IT世界,在知识的湖畔探索吧!function getSong() {
  let _song = '';
  let i = 100;
  for (i; i > 0; i -= 1) {
  /* eslint-disable no-loop-func */
    setTimeout(function () {
    _song += `${i} beers on the wall, you take one down and pass it around, ${
    i - 1
    } bottles of beer on the wall\n`;
    if (i === 1) {
    _song += "Hey let's get some more beer";
    }
    }, 0);
  /* eslint-enable no-loop-func */
  }
  return _song;
}
function singSong(_song) {
if (!_song) throw new Error("song is '' empty, FEED ME A SONG!");
console.log(_song);
}
const song = getSong('beer');
// this will not work
singSong(song);
// Uncaught Error: song is '' empty, FEED ME A SONG!

为什么会发生这种情况?setTimeout指示CPU将指令存储在总线上的其他位置,并指示将数据安排为稍后处理。在函数在0毫秒标记处再次命中之前,经过了数千个CPU周期,CPU从总线中获取指令并执行它们。唯一的问题是song(“”)在数千个循环之前被返回。

在处理文件系统和网络请求时也会出现同样的情况。主线程不能在不确定的时间段内被阻塞——因此,我们使用回调来以可控的方式及时调度代码的执行。

我们可以使用以下3种模式执行几乎所有的操作:

1、串联:函数将以严格的顺序执行,这一顺序与循环最相似。

// operations defined elsewhere and ready to execute
const operations = [
  { func: function1, args: args1 },
  { func: function2, args: args2 },
  { func: function3, args: args3 },
];
function executeFunctionWithArgs(operation, callback) {
// executes function
const { args, func } = operation;
	func(args, callback);
}
function serialProcedure(operation) {
  if (!operation) process.exit(0); // finished
    executeFunctionWithArgs(operation, function (result) {
    // continue AFTER callback
    serialProcedure(operations.shift());
  });
}
serialProcedure(operations.shift());

2、完全并行-用于同时运行异步任务

let count = 0;
let success = 0;
const failed = [];
const recipients = [
  { name: 'Bart', email: 'bart@tld' },
  { name: 'Marge', email: 'marge@tld' },
  { name: 'Homer', email: 'homer@tld' },
  { name: 'Lisa', email: 'lisa@tld' },
  { name: 'Maggie', email: 'maggie@tld' },
];

function dispatch(recipient, callback) {
  // `sendEmail` is a hypothetical SMTP client
  sendMail(
    {
      subject: 'Dinner tonight',
      message: 'We have lots of cabbage on the plate. You coming?',
      smtp: recipient.email,
    },
    callback
  );
}

function final(result) {
  console.log(`Result: ${result.count} attempts \
      & ${result.success} succeeded emails`);
  if (result.failed.length)
    console.log(`Failed to send to: \
        \n${result.failed.join('\n')}\n`);
}

recipients.forEach(function (recipient) {
  dispatch(recipient, function (err) {
    if (!err) {
      success += 1;
    } else {
      failed.push(recipient.name);
    }
    count += 1;

    if (count === recipients.length) {
      final({
        count,
        success,
        failed,
      });
    }
  });
});

3、有限并行:一种异步、并行、并发受限的循环,例如成功地向10E7用户列表中的1000000个收件人发送电子邮件。

let successCount = 0;
function final() {
  console.log(`dispatched ${successCount} emails`);
  console.log('finished');
}
function dispatch(recipient, callback) {
// `sendEmail` is a hypothetical SMTP client
sendMail(
  {
  subject: 'Dinner tonight',
  message: 'We have lots of cabbage on the plate. You coming?',
  smtp: recipient.email,
  },
  callback
);
}
function sendOneMillionEmailsOnly() {
getListOfTenMillionGreatEmails(function (err, bigList) {
  if (err) throw err;
  function serial(recipient) {
    if (!recipient || successCount >= 1000000) return final();
    dispatch(recipient, function (_err) {
      if (!_err) successCount += 1;
        serial(bigList.pop());
      });
  }
  serial(bigList.pop());
  });
}
sendOneMillionEmailsOnly();

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/14048.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信