call(actionName, params, _opts) {
		const opts = Object.assign(
			{
				parentCtx: this
			},
			_opts
		);

		if (this.options.timeout > 0 && this.startHrTime) {
			// Distributed timeout handling. Decrementing the timeout value with the elapsed time.
			// If the timeout below 0, skip the call.
			const diff = process.hrtime(this.startHrTime);
			const duration = diff[0] * 1e3 + diff[1] / 1e6;
			const distTimeout = this.options.timeout - duration;

			if (distTimeout <= 0) {
				return this.broker.Promise.reject(
					new RequestSkippedError({ action: actionName, nodeID: this.broker.nodeID })
				);
			}

			if (!opts.timeout || distTimeout < opts.timeout) opts.timeout = distTimeout;
		}

		// Max calling level check to avoid calling loops
		if (
			this.broker.options.maxCallLevel > 0 &&
			this.level >= this.broker.options.maxCallLevel
		) {
			return this.broker.Promise.reject(
				new MaxCallLevelError({ nodeID: this.broker.nodeID, level: this.level })
			);
		}

		let p = this.broker.call(actionName, params, opts);

		// Merge metadata with sub context metadata
		return p
			.then(res => {
				if (p.ctx) mergeMeta(this, p.ctx.meta);

				return res;
			})
			.catch(err => {
				if (p.ctx) mergeMeta(this, p.ctx.meta);

				return this.broker.Promise.reject(err);
			});
	}
Call an other action. It creates a sub-context.

@param {String} actionName
@param {Object?} params
@param {Object?} opts
@returns {Promise}

@example <caption>Call an other service with params & options</caption>
ctx.call("posts.get", { id: 12 }, { timeout: 1000 });

@memberof Context