diff --git a/src/core/Service.js b/src/core/Service.js index a30236aaf..1fe8935ba 100644 --- a/src/core/Service.js +++ b/src/core/Service.js @@ -126,4 +126,37 @@ export default class Service extends EventEmitter { this.ros.callOnConnection(call); } + + /** + * An alternate form of Service advertisement that supports a modern Promise-based interface for use with async/await. + * @param {(request: TRequest) => Promise} callback An asynchronous callback processing the request and returning a response. + */ + advertiseAsync(callback) { + if (this.isAdvertised) { + throw new Error('Cannot advertise the same Service twice!'); + } + this.ros.on(this.name, async (rosbridgeRequest) => { + /** @type {{op: string, service: string, values?: TResponse, result: boolean, id?: string}} */ + let rosbridgeResponse = { + op: 'service_response', + service: this.name, + result: false + } + try { + rosbridgeResponse.values = await callback(rosbridgeRequest.args); + rosbridgeResponse.result = true; + } finally { + if (rosbridgeRequest.id) { + rosbridgeResponse.id = rosbridgeRequest.id; + } + this.ros.callOnConnection(rosbridgeResponse); + } + }); + this.ros.callOnConnection({ + op: 'advertise_service', + type: this.serviceType, + service: this.name + }); + this.isAdvertised = true; + } } diff --git a/test/service.test.js b/test/service.test.js new file mode 100644 index 000000000..e9d9aba15 --- /dev/null +++ b/test/service.test.js @@ -0,0 +1,28 @@ +import { it, describe, expect } from 'vitest'; +import { Service, Ros } from '../'; + +describe('Service', () => { + const ros = new Ros({ + url: 'ws://localhost:9090' + }); + it('Successfully advertises a service with an async return', async () => { + const server = new Service({ + ros, + serviceType: 'std_srvs/Trigger', + name: '/test_service' + }); + server.advertiseAsync(async () => { + return { + success: true, + message: 'foo' + } + }); + const client = new Service({ + ros, + serviceType: 'std_srvs/Trigger', + name: '/test_service' + }) + const response = await new Promise((resolve, reject) => client.callService({}, resolve, reject)); + expect(response).toEqual({success: true, message: 'foo'}); + }) +})