前言
本文主要解读ControlService的sendTwoWay和sendOneWay的区别,sendTwoWay函数怎么实现双向通信的。
touchDown与install
我们看到 res/app/components/stf/screen/screen-directive.js有这样的调用方式
control.touchDown(nextSeq(), 0, scaled.xP, scaled.yP, pressure)
其调用的res/app/components/stf/control/control-service.js
this.touchDown = function(seq, contact, x, y, pressure) {
sendOneWay('input.touchDown', {
seq: seq
, contact: contact
, x: x
, y: y
, pressure: pressure
})
}
而 res/app/components/stf/install/install-service.js
control.install({
href: installation.href
, manifest: installation.manifest
, launch: installation.launch
})
.progressed(function(result) {
installation.update(50 + result.progress / 2, result.lastData)
})
.then(function(result) {
})
首先跟touchdown的区别是control.install可以后续通过progressed获取进度,通过then获取result。那为什么control.install可以发出消息后获取到后台返回的信息呢?
然后再看res/app/components/stf/control/control-service里面
this.install = function(options) {
return sendTwoWay('device.install', options)
}
出乎意料的简单,这也看不出哪能返回后台处理后的信息呀。
关键点就是 control.install调用的是sendTwoWay(), 而control.touchDown调用的是sendOneWay().
sendOneWay与sendTwoWay
而sendTwoWay()和sendOneWay()再看下源码:
function ControlService(target, channel) {
function sendOneWay(action, data) {
socket.emit(action, channel, data)
}
function sendTwoWay(action, data) {
var tx = TransactionService.create(target)
socket.emit(action, channel, tx.channel, data)
return tx.promise
}
......
}
sendOneWay,了解过socket.io的用法的都知道 socket.emit是发消息给后台, 要想接收后台发回的信息是要socket.on('xxxx,xxxx)的。 而sendOneWay只有socket.emit,没有socket.on(),所以只是单向发消息而已。
那sendTwoWay是怎么双向通信的呢? 它多了个 TransactionService, socket.emit多传了个 tx.channel的参数,而且返回的是tx.promise。
疑点TransactionService
那我们就进 res/app/components/stf/transaction/transaction-service.js看下
module.exports = function TransactionServiceFactory(socket, TransactionError) {
var transactionService = {}
function createChannel() {
return 'tx.' + uuid.v4()
}
......
//sendTwoWay调用TransactionService.create(target)生成一个tx实例
transactionService.create = function(target, options) {
if (options && !options.result) {
options.result = TransactionResult
}
if (Array.isArray(target)) {
return new MultiTargetTransaction(target, options || {
result: DeviceTransactionResult
, id: 'serial'
})
}
else {//target唯一,生成SingleTargetTransaction对象
return new SingleTargetTransaction(target, options || {
result: DeviceTransactionResult
, id: 'serial'
})
}
}
那追踪看下 SingleTargetTransaction
function SingleTargetTransaction(target, options) {
var result = new options.result(target)
var pending = new PendingTransactionResult(result)
var channel = createChannel()
function doneListener(someChannel, data) {
if (someChannel === channel) {
pending.done(data)
}
}
function progressListener(someChannel, data) {
if (someChannel === channel) {
pending.progress(data)
}
}
function cancelListener(someChannel, data) {
if (someChannel === channel) {
pending.cancel(data)
}
}
//监听了tx.done消息类型
socket.on('tx.done', doneListener)
//监听了tx.progress消息类型
socket.on('tx.progress', progressListener)
socket.on('tx.cancel', cancelListener)
this.channel = channel
this.result = result
this.results = [result]
//并且有promise属性,progressed和then返回了结果。
this.promise = pending.promise
.finally(function() {
socket.removeListener('tx.done', doneListener)
socket.removeListener('tx.progress', progressListener)
socket.removeListener('tx.cancel', cancelListener)
socket.emit('tx.cleanup', channel)
})
.progressed(function() {
return result
})
.then(function() {
return result
})
}
那control.install接收tx.promise就包含了progressed和then的返回result。
所以就看 后台是不是发送的tx.done消息就行了。 这不完成了双向消息打通。
后台的消息发哪去了
install的后台程序是lib/units/device/plugins/install.js
//安装完成
push.send([
channel
, reply.okay('INSTALL_SUCCEEDED')
])
//安装进度
push.send([
channel
, reply.progress(data, progress)
])
追踪下reply的okay和progress函数
lib/wire/util.js
reply: function(source) {
var seq = 0
return {
okay: function(data, body) {
return wireutil.envelope(new wire.TransactionDoneMessage(
source
, seq++
, true
, data === null ? null : (data || 'success')
, body ? JSON.stringify(body) : null
))
}
, progress: function(data, progress) {
return wireutil.envelope(new wire.TransactionProgressMessage(
source
, seq++
, data
, ~~progress
))
}
}
}
一个是包装成了TransactionDoneMessage,一个是TransactionProgressMessage。
那看下谁接收了这两个message.
lib/units/processor/index.js
.on(wire.TransactionProgressMessage, function(channel, message, data) {
appDealer.send([channel, data])
})
.on(wire.TransactionDoneMessage, function(channel, message, data) {
appDealer.send([channel, data])
})
然后lib/units/websocket/index.js
.on(wire.TransactionProgressMessage, function(channel, message) {
socket.emit('tx.progress', channel.toString(), message)
})
.on(wire.TransactionDoneMessage, function(channel, message) {
socket.emit('tx.done', channel.toString(), message)
})
我们看到了tx.progress和tx.done消息类型,正是前面TransactionService里监听处理的,然后以tx.promise的方式将progress和done/success传给了前端对control.install的调用。所以control.install可以再接Progressed()拿到进度数据,接then获取到success数据。