Nodejs中的Javascript异步编程模式

Nodejs中的Javascript异步编程模式

什么非阻塞,异步I/O就不多说了。这里简单概括一下js的异步编程模式,但是别指望下面的东西能让你学会什么。最好的学习方式是实践。

PubSub模式,也就是订阅分发。

Node的API构架师因为太喜欢PubSub,所以就直接有了一个实体叫EventEmitter对象,几乎所有的I/O对象都继承了EvenEmitter。事件->事件处理器,是最基本的js设计模式。

Promise

尽管PubSub模式已经非常多才多艺,但是并不是适用于所有任务的万能工具,尤其是解决类似一次性事件的问题。比如对一次性任务的两种结果做不同的处理。 在之前的jQuery版本里

$.get('/mydata',{
  succeess:onSuccess,
  failure:onFail,
  always:onAlways
})

在新版本的jQuery里

var promise = $.get('/mydata');
promise.done(onSuccess);
promise.fail(onFailure);
promise.always(onAlways);

jQuery的promise和CommmonJs的Promises/A大部分是一样的。 promise 还可以进行合并,比如

gameReady = $.when(tutorialPromise,gameLoadedPromise)
gameReady.done(startGame)

Promise越来越流行,就有越来越多的js库会要求异步函数必须返回promise对象,代替回调函数。

Async.js 工作流控制

前面都是对对象的抽象设计来控制异步的布局。但是,假设需要执行一组IO操作(或者并行,或者串行),该怎么办呢? 这个问题在Nodejs里面的确是必须面对的事情。以至于有了一个专有名词流程控制。Async.js是npm里面,人气相当高的 一个流程控制库。类似的还有一些轻量级的比如eventproxy.js,和step.js,都是非常棒的流程控制库。 比如

async.filter async.foreach 会并行处理数组
async.filterseries async.foreachSeries 会顺序处理数组
reject/rejectSeries 和filter 相反
map/mapSeries 1:1映射
reduce/reduceRight 值的逐步变换
detect/detectSeries 找到筛选器匹配的值
sortBy 产生有序副本
some 测试至少有一个值符合给定标准
every 测试是否所有值均符合给定标准

Async.js的精髓就是能够以最低的代码重复度来执行常见的迭代工作。

还有例如

async.series 异步函数顺序执行
async.parallel 异步函数并行执行
async.queue 动态队列排队技术,如下 
async = require 'async'

worker (data,cb){ console.log(data) cb() }

concurrency = 2 queue = async.queue(worker,concurrency) queue.push(1) queue.push(2) queue.push(3)

只要并发度不小于1,结果都是

1
2
3

区别是并发度为1需要三轮遍历,并发度为2需要2两轮遍历才能遍历完,并发度大于3的话,一轮就够了。

如果你的应用需要工作流程控制,那就需要找一个好的轮子,并且掌握它,我推荐上面提到的那些流程控制库。

worker对象的多线程技术

事件是多线程技术的替代品,但是多核cpu盛行的当下,我们需要多线程编程技术。那是否意味着就要放弃基于事件的编程呢?

多核任务调度是非常麻烦的,为了保持程序的纯洁性,最好让一个cpu单独执行一个任务,只是偶尔同步一下。 js中的worker就是这么干的。

网页版中的worker是HTML5标准的一部分,下面是个例子

//mian script
var worker = new Worker('boknows.js')
worker.addEventLisener('message',function(e){
  console.log(e.data)
})
worker.postMessage('football')
worker.postMessage('baseball')

//boknow.js

self.addEventListener(‘message’,function(e){ self.postMessage(‘Bo Nonws’+e.data) })

在nodejs中cluster就是nodejs版的worker

cluster = 'cluster'

if(cluster.isMaster){
  coreCount = require('os').cpus().length;
  for(i=0;i<coreCount;i++){
  cluster.fork()
  cluster.on('death',function(worker){
     console.log('Worker '+worker.pid+'has died')
   })
  }
}else{
 process.exit()
}

输出如下

worker 15530 has died
worker 15532 has died
worker 15529 has died
worker 15531 has died

每一行输出对应一个cpu,但是worker到底是不是分配在一个独立的cpu上还要看底层的操作系统。

异步脚本加载

很多时候,我们需要为把.js放在head还是放在body后面担忧。利用defer,async这些属性也能处理很多情况。

这个时候可编程的条件加载出现了。比如yepnope这种轻量的脚本加载库

yepnope({
  load:'js.js',
  callback:function(){
    console.log('ready')
  }
})

还有Require.js/AMD的智能加载 加载示例

require(['moment'],function(moment){
 console.log(moment().format('dddd'))
})

AMD模块定义示例

define('myApplication',['jquery'],function(){
  $('<body>').append('<p>hello,async world</p>')
})

这样可以解决模块加载依赖等问题。


异步模式大概如此,但远不止如此。


4 回复

Nodejs中的JavaScript异步编程模式

PubSub模式

Node.js 的 API 架构师非常喜欢发布/订阅(PubSub)模式,因此引入了一个名为 EventEmitter 的实体,几乎所有的 I/O 对象都继承自 EventEmitter。事件与事件处理器是 JavaScript 中最基本的设计模式之一。

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
  console.log('an event occurred!');
});

myEmitter.emit('event'); // 输出: an event occurred!

Promise

尽管 EventEmitter 模式非常强大,但它并不适合所有场景,特别是处理一次性事件的任务。在这种情况下,我们可以使用 Promise 来处理。

const getMyData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Data fetched successfully');
    }, 2000);
  });
};

getMyData()
  .then(data => {
    console.log(data); // 输出: Data fetched successfully
  })
  .catch(error => {
    console.error(error);
  });

Async.js 工作流控制

对于一组 I/O 操作(并行或串行),我们可以使用 async.js 库来进行工作流控制。

const async = require('async');

async.parallel([
  function(callback) {
    setTimeout(() => {
      console.log('Task 1 completed');
      callback(null, 'Result 1');
    }, 1000);
  },
  function(callback) {
    setTimeout(() => {
      console.log('Task 2 completed');
      callback(null, 'Result 2');
    }, 2000);
  }
], function(err, results) {
  console.log(results); // 输出: [ 'Result 1', 'Result 2' ]
});

多线程技术

虽然事件驱动模型很强大,但在多核 CPU 环境下,我们可能需要更强大的多线程技术。Node.js 提供了 cluster 模块来实现这一需求。

const cluster = require('cluster');
const os = require('os');

if (cluster.isMaster) {
  const cpuCount = os.cpus().length;

  for (let i = 0; i < cpuCount; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);
  });
} else {
  // 子进程的工作逻辑
}

异步脚本加载

为了优化页面加载性能,我们可以使用 yepnopeRequire.js 进行异步脚本加载。

yepnope({
  load: 'js.js',
  callback: function() {
    console.log('Script loaded and executed');
  }
});
require(['moment'], function(moment) {
  console.log(moment().format('dddd')); // 输出当前日期
});

结论

以上只是异步编程模式的一部分内容。Node.js 提供了多种工具来处理异步编程,掌握这些工具将帮助你编写高效、可维护的代码。希望本文能为你提供一些有用的参考。


嗯 目前成熟的就差不多这些

总结到位,哈哈。 在nodejs里边用的多的是基于回调的async和基于事件的eventproxy 浏览器端使用requirejs解决模块依赖是趋势,业务逻辑里的异步和nodejs差不多

Nodejs中的JavaScript异步编程模式

1. PubSub模式(订阅分发)

Node.js中的EventEmitter对象几乎被所有I/O对象继承,它提供了事件触发与监听机制。通过这种方式,你可以订阅某个事件并在事件触发时执行相应的处理器。

示例代码:

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();

myEmitter.on('event', () => {
  console.log('An event occurred!');
});

myEmitter.emit('event'); // 输出: An event occurred!

2. Promise

Promise是一种处理异步操作的方式,它可以更优雅地处理成功或失败的结果。

示例代码:

const fetch = require('node-fetch');

const promise = fetch('https://jsonplaceholder.typicode.com/todos/1');

promise.then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));

3. Async.js 工作流控制

Async.js 是一个流行的库,用于处理复杂的异步操作,如并行或顺序执行异步函数。

示例代码:

const async = require('async');

async.parallel([
  function(callback) {
    setTimeout(() => callback(null, 'one'), 200);
  },
  function(callback) {
    setTimeout(() => callback(null, 'two'), 100);
  }
], function(err, results) {
  console.log(results); // 输出: [ 'one', 'two' ]
});

4. 多线程技术

在Node.js中,cluster模块可以用来创建多个工作进程,每个进程都在独立的CPU核心上运行。

示例代码:

const cluster = require('cluster');
const os = require('os');

if (cluster.isMaster) {
  const coreCount = os.cpus().length;
  for (let i = 0; i < coreCount; i++) {
    cluster.fork();
  }
} else {
  console.log(`Worker ${process.pid} started`);
}

5. 异步脚本加载

使用yepnopeRequire.js可以实现更灵活的脚本加载策略。

示例代码:

const yepnope = require('yepnope');

yepnope({
  load: 'script.js',
  complete: function() {
    console.log('Script loaded and executed.');
  }
});

总结

以上介绍了几种在Node.js中常用的异步编程模式。通过理解和使用这些模式,可以更有效地管理复杂的异步逻辑,提升应用性能和可维护性。

回到顶部