簡單的程序,不準要考慮通訊問題,在一個進程中函數調用便可知足程序功能的分治工做,javascript
可是當程序規模達到必定程序以後, 就必須將程序劃分爲獨立的邏輯體, 邏輯體之間須要進行通訊。html
本文列舉程序通訊方式,從微觀到宏觀。vue
經過事件,能夠將單線程的程序,作好邏輯切分的狀況下,產生通訊過程,同時保持邏輯解耦。java
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/EventTargetnode
EventTarget 的 addEventListener 方法添加事件, dispatchEvent 分發事件。
class MyEventTarget extends EventTarget { constructor(mySecret) { super(); this._secret = mySecret; } get secret() { return this._secret; } }; let myEventTarget = new MyEventTarget(5); let value = myEventTarget.secret; // == 5 myEventTarget.addEventListener("foo", function(e) { this._secret = e.detail; }); let event = new CustomEvent("foo", { detail: 7 }); myEventTarget.dispatchEvent(event); let newValue = myEventTarget.secret; // == 7
標準:linux
https://dom.spec.whatwg.org/#dom-eventtarget-eventtargetgit
target = new EventTarget();
Creates a new
EventTarget
object, which can be used by developers to dispatch and listen for events.githubtarget . addEventListener(type, callback [, options])
Appends an event listener for events whose
type
attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched.springThe options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options’s
capture
.編程When set to true, options’s
capture
prevents callback from being invoked when the event’seventPhase
attribute value isBUBBLING_PHASE
. When false (or not present), callback will not be invoked when event’seventPhase
attribute value isCAPTURING_PHASE
. Either way, callback will be invoked if event’seventPhase
attribute value isAT_TARGET
.When set to true, options’s
passive
indicates that the callback will not cancel the event by invokingpreventDefault()
. This is used to enable performance optimizations described in §2.8 Observing event listeners.When set to true, options’s
once
indicates that the callback will only be invoked once after which the event listener will be removed.The event listener is appended to target’s event listener list and is not appended if it has the same type, callback, and capture.
target . removeEventListener(type, callback [, options])
Removes the event listener in target’s event listener list with the same type, callback, and options.
target . dispatchEvent(event)
Dispatches a synthetic event event to target and returns true if either event’s
cancelable
attribute value is false or itspreventDefault()
method was not invoked, and false otherwise.
http://javascript.ruanyifeng.com/nodejs/events.html
const EventEmitter = require('events'); class MyEmitter extends EventEmitter {} const myEmitter = new MyEmitter(); myEmitter.on('event', () => { console.log('an event occurred!'); }); myEmitter.emit('event');
標準:
https://nodejs.org/api/events.html#events_class_eventemitter
Much of the Node.js core API is built around an idiomatic asynchronous event-driven architecture in which certain kinds of objects (called "emitters") emit named events that cause
Function
objects ("listeners") to be called.For instance: a
net.Server
object emits an event each time a peer connects to it; afs.ReadStream
emits an event when the file is opened; a stream emits an event whenever data is available to be read.All objects that emit events are instances of the
EventEmitter
class. These objects expose aneventEmitter.on()
function that allows one or more functions to be attached to named events emitted by the object. Typically, event names are camel-cased strings but any valid JavaScript property key can be used.When the
EventEmitter
object emits an event, all of the functions attached to that specific event are called synchronously. Any values returned by the called listeners are ignored and will be discarded.The following example shows a simple
EventEmitter
instance with a single listener. TheeventEmitter.on()
method is used to register listeners, while theeventEmitter.emit()
method is used to trigger the event.
https://www.dev2qa.com/spring-event-publisher-listener-example/
https://alligator.io/vuejs/global-event-bus/
event-bus.js
import Vue from 'vue'; export const EventBus = new Vue();
linstener
// Import the EventBus. import { EventBus } from './event-bus.js'; // Listen for the i-got-clicked event and its payload. EventBus.$on('i-got-clicked', clickCount => { console.log(`Oh, that's nice. It's gotten ${clickCount} clicks! :)`) });
publisher
// Import the EventBus we just created. import { EventBus } from './event-bus.js'; // The event handler function. const clickHandler = function(clickCount) { console.log(`Oh, that's nice. It's gotten ${clickCount} clicks! :)`) } // Listen to the event. EventBus.$on('i-got-clicked', clickHandler); // Stop listening. EventBus.$off('i-got-clicked', clickHandler);
https://cn.vuejs.org/v2/api/#%E5%AE%9E%E4%BE%8B%E6%96%B9%E6%B3%95-%E4%BA%8B%E4%BB%B6
這種用法有點hacker味道, 非Vue的本來設計。
因此有人專門爲Vue實現了 EventBus 庫。
https://github.com/cklmercer/vue-events
import Vue from 'vue' import VueEvents from 'vue-events' Vue.use(VueEvents)
new Vue({ data() { return { eventData: { foo: 'baz' } } }, mounted() { this.$events.fire('testEvent', this.eventData); this.$events.emit('testEvent', this.eventData); this.$events.$emit('testEvent', this.eventData); } })
new Vue({ mounted() { this.$events.on('testEvent', eventData => console.log(eventData)); }, beforeDestroy() { this.$events.$off('testEvent') this.$events.off('testEvent') this.$events.remove('testEvent') } })
https://github.com/yangmingshan/vue-bus
// ... created() { this.$bus.on('add-todo', this.addTodo); this.$bus.once('once', () => console.log('This listener will only fire once')); }, beforeDestroy() { this.$bus.off('add-todo', this.addTodo); }, methods: { addTodo(newTodo) { this.todos.push(newTodo); } }
// ... methods: { addTodo() { this.$bus.emit('add-todo', { text: this.newTodoText }); this.$bus.emit('once'); this.newTodoText = ''; } }
https://github.com/google/guava/wiki/EventBusExplained
EventBus
allows publish-subscribe-style communication between components without requiring the components to explicitly register with one another (and thus be aware of each other). It is designed exclusively to replace traditional Java in-process event distribution using explicit registration. It is not a general-purpose publish-subscribe system, nor is it intended for interprocess communication.
he
EventBus
system and code use the following terms to discuss event distribution:
Event Any object that may be posted to a bus. Subscribing The act of registering a listener with an EventBus
, so that its handler methods will receive events.Listener An object that wishes to receive events, by exposing handler methods. Handler method A public method that the EventBus
should use to deliver posted events. Handler methods are marked by the@Subscribe
annotation.Posting an event Making the event available to any listeners through the EventBus
.
https://www.jianshu.com/p/f9ae5691e1bb
EventBus可以簡化各組件間的通訊,讓咱們的代碼書寫變得簡單,能有效的分離事件發送方和接收方(也就是解耦的意思),能避免複雜和容易出錯的依賴性和生命週期問題。
三要素
- Event 事件。它能夠是任意類型。
- Subscriber 事件訂閱者。在EventBus3.0以前咱們必須定義以onEvent開頭的那幾個方法,分別是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0以後事件處理的方法名能夠隨意取,不過須要加上註解@subscribe(),而且指定線程模型,默認是POSTING。
- Publisher 事件的發佈者。咱們能夠在任意線程裏發佈事件,通常狀況下,使用EventBus.getDefault()就能夠獲得一個EventBus對象,而後再調用post(Object)方法便可。
http://greenrobot.org/eventbus/
EventBus is an open-source library for Android and Java using the publisher/subscriber pattern for loose coupling. EventBus enables central communication to decoupled classes with just a few lines of code – simplifying the code, removing dependencies, and speeding up app development.
https://www.zhihu.com/question/23486749
發佈訂閱模式是最經常使用的一種觀察者模式的實現,而且從解耦和重用角度來看,更優於典型的觀察者模式
在觀察者模式中,觀察者須要直接訂閱目標事件;在目標發出內容改變的事件後,直接接收事件並做出響應
╭─────────────╮ Fire Event ╭──────────────╮
│ │─────────────>│ │
│ Subject │ │ Observer │
│ │<─────────────│ │
╰─────────────╯ Subscribe ╰──────────────╯
在發佈訂閱模式中,發佈者和訂閱者之間多了一個發佈通道;一方面從發佈者接收事件,另外一方面向訂閱者發佈事件;訂閱者須要從事件通道訂閱事件
以此避免發佈者和訂閱者之間產生依賴關係
╭─────────────╮ ╭───────────────╮ Fire Event ╭──────────────╮
│ │ Publish Event │ │───────────────>│ │
│ Publisher │────────────────>│ Event Channel │ │ Subscriber │
│ │ │ │<───────────────│ │
╰─────────────╯ ╰───────────────╯ Subscribe ╰──────────────╯
做者:我曾跨過山和大海
連接:https://www.zhihu.com/question/23486749/answer/186049081
來源:知乎
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
訂閱/發佈 模式重點是廣播外的消息,這個模式並不關心誰接收事件,只管發送事件。
Let’s talk about two concepts that seem similar: the Observer pattern and the Pub-sub pattern.
Observer pattern
This is a pattern of development in which your class or primary object (known as the Observable) notifies other interested classes or objects (known as Observers) with relevant information (events).
Pub-sub pattern
The objective of the pub-sub pattern is exactly the same as the Observer pattern viz. you want some other class to know of certain events taking place.
There’s an important semantic difference between the Observer and Pub-sub patterns though: in the pub-sub pattern the focus is on 「broadcasting」 messages outside. The Observable here doesn’t want to know who the events are going out to, just that they’ve gone out. In other words the Observable (a.k.a Publisher) doesn’t want to know who the Observers (a.k.a Subscribers) are.
https://www.cnblogs.com/lovesong/p/5272752.html
var pubsub = {}; (function(myObject) { // Storage for topics that can be broadcast // or listened to var topics = {}; // An topic identifier var subUid = -1; // Publish or broadcast events of interest // with a specific topic name and arguments // such as the data to pass along myObject.publish = function( topic, args ) { if ( !topics[topic] ) { return false; } var subscribers = topics[topic], len = subscribers ? subscribers.length : 0; while (len--) { subscribers[len].func( topic, args ); } return this; }; // Subscribe to events of interest // with a specific topic name and a // callback function, to be executed // when the topic/event is observed myObject.subscribe = function( topic, func ) { if (!topics[topic]) { topics[topic] = []; } var token = ( ++subUid ).toString(); topics[topic].push({ token: token, func: func }); return token; }; // Unsubscribe from a specific // topic, based on a tokenized reference // to the subscription myObject.unsubscribe = function( token ) { for ( var m in topics ) { if ( topics[m] ) { for ( var i = 0, j = topics[m].length; i < j; i++ ) { if ( topics[m][i].token === token ) { topics[m].splice( i, 1 ); return token; } } } } return this; }; }( pubsub ));
https://www.freedesktop.org/wiki/Software/dbus/
D-Bus is a message bus system, a simple way for applications to talk to one another. In addition to interprocess communication, D-Bus helps coordinate process lifecycle; it makes it simple and reliable to code a "single instance" application or daemon, and to launch applications and daemons on demand when their services are needed.
D-Bus supplies both a system daemon (for events such as "new hardware device added" or "printer queue changed") and a per-user-login-session daemon (for general IPC needs among user applications). Also, the message bus is built on top of a general one-to-one message passing framework, which can be used by any two apps to communicate directly (without going through the message bus daemon). Currently the communicating applications are on one computer, or through unencrypted TCP/IP suitable for use behind a firewall with shared NFS home directories. (Help wanted with better remote transports - the transport mechanism is well-abstracted and extensible.)
The dbus low-level API reference implementation and the D-Bus protocol have been heavily tested in the real world over several years, and are now "set in stone." Future changes will either be compatible or versioned appropriately.
https://www.ibm.com/developerworks/cn/linux/l-dbus.html
D-BUS 本質上是 進程間通訊(inter-process communication)(IPC)的一個實現。不過,有一些 特性使得 D-BUS 遠遠不是「只是另外一個 IPC 實現」。有不少不一樣的 IPC 實現,由於每個都定位於解決 特定的明肯定義的問題。CORBA 是用於面向對象編程中複雜的 IPC 的一個強大的解決方案。DCOP 是一個 較輕量級的 IPC 框架,功能較少,可是能夠很好地集成到 K 桌面環境中。SOAP 和 XML-RPC 設計用於 Web 服務,於是使用 HTTP 做爲其傳輸協議。D-BUS 設計用於桌面應用程序和 OS 通訊。
http://www.rabbitmq.com/#features
RabbitMQ is the most widely deployed open source message broker.
With more than 35,000 production deployments of RabbitMQ world-wide at small startups and large enterprises, RabbitMQ is the most popular open source message broker.
RabbitMQ is lightweight and easy to deploy on premises and in the cloud. It supports multiple messaging protocols. RabbitMQ can be deployed in distributed and federated configurations to meet high-scale, high-availability requirements.
http://www.rabbitmq.com/getstarted.html
RabbitMQ is a message broker: it accepts and forwards messages. You can think about it as a post office: when you put the mail that you want posting in a post box, you can be sure that Mr. or Ms. Mailperson will eventually deliver the mail to your recipient. In this analogy, RabbitMQ is a post box, a post office and a postman.
The major difference between RabbitMQ and the post office is that it doesn't deal with paper, instead it accepts, stores and forwards binary blobs of data ‒ messages.
This tutorial assumes RabbitMQ is installed and running on localhost on standard port (5672). In case you use a different host, port or credentials, connections settings would require adjusting.
If you're having trouble going through this tutorial you can contact us through the mailing list.
RabbitMQ is a message broker: it accepts and forwards messages. You can think about it as a post office: when you put the mail that you want posting in a post box, you can be sure that Mr. or Ms. Mailperson will eventually deliver the mail to your recipient. In this analogy, RabbitMQ is a post box, a post office and a postman.
The major difference between RabbitMQ and the post office is that it doesn't deal with paper, instead it accepts, stores and forwards binary blobs of data ‒ messages.
RabbitMQ, and messaging in general, uses some jargon.
Producing means nothing more than sending. A program that sends messages is a producer :
A queue is the name for a post box which lives inside RabbitMQ. Although messages flow through RabbitMQ and your applications, they can only be stored inside a queue. A queue is only bound by the host's memory & disk limits, it's essentially a large message buffer. Many producers can send messages that go to one queue, and many consumers can try to receive data from one queue. This is how we represent a queue:
Consuming has a similar meaning to receiving. A consumer is a program that mostly waits to receive messages:
Note that the producer, consumer, and broker do not have to reside on the same host; indeed in most applications they don't.
In this part of the tutorial we'll write two small programs in Javascript; a producer that sends a single message, and a consumer that receives messages and prints them out. We'll gloss over some of the detail in the amqp.node API, concentrating on this very simple thing just to get started. It's a "Hello World" of messaging.
In the diagram below, "P" is our producer and "C" is our consumer. The box in the middle is a queue - a message buffer that RabbitMQ keeps on behalf of the consumer.
https://github.com/rabbitmq/rabbitmq-tutorials/blob/master/javascript-nodejs/src/receive.js
#!/usr/bin/env node var amqp = require('amqplib/callback_api'); amqp.connect('amqp://localhost', function(err, conn) { conn.createChannel(function(err, ch) { var q = 'hello'; ch.assertQueue(q, {durable: false}); console.log(" [*] Waiting for messages in %s. To exit press CTRL+C", q); ch.consume(q, function(msg) { console.log(" [x] Received %s", msg.content.toString()); }, {noAck: true}); }); });
#!/usr/bin/env node var amqp = require('amqplib/callback_api'); amqp.connect('amqp://localhost', function(err, conn) { conn.createChannel(function(err, ch) { var q = 'hello'; var msg = 'Hello World!'; ch.assertQueue(q, {durable: false}); ch.sendToQueue(q, Buffer.from(msg)); console.log(" [x] Sent %s", msg); }); setTimeout(function() { conn.close(); process.exit(0) }, 500); });