Skip to content

Commit 0aac975

Browse files
committed
Remove support for context.done
1 parent c3eab14 commit 0aac975

File tree

6 files changed

+16
-170
lines changed

6 files changed

+16
-170
lines changed

src/Context.ts

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,13 @@ import { fromRpcTraceContext, fromTypedData } from './converters/RpcConverters';
2121
import { FunctionInfo } from './FunctionInfo';
2222
import { Request } from './http/Request';
2323
import { Response } from './http/Response';
24-
import EventEmitter = require('events');
2524

2625
export function CreateContextAndInputs(
2726
info: FunctionInfo,
2827
request: RpcInvocationRequest,
29-
userLogCallback: UserLogCallback,
30-
doneEmitter: EventEmitter
28+
userLogCallback: UserLogCallback
3129
) {
32-
const context = new InvocationContext(info, request, userLogCallback, doneEmitter);
30+
const context = new InvocationContext(info, request, userLogCallback);
3331

3432
const bindings: ContextBindings = {};
3533
const inputs: any[] = [];
@@ -56,7 +54,7 @@ export function CreateContextAndInputs(
5654
context.bindings = bindings;
5755
if (httpInput) {
5856
context.req = httpInput;
59-
context.res = new Response(context.done);
57+
context.res = new Response();
6058
// This is added for backwards compatability with what the host used to send to the worker
6159
context.bindingData.sys = {
6260
methodName: info.name,
@@ -87,14 +85,8 @@ class InvocationContext implements Context {
8785
log: Logger;
8886
req?: Request;
8987
res?: Response;
90-
done: DoneCallback;
9188

92-
constructor(
93-
info: FunctionInfo,
94-
request: RpcInvocationRequest,
95-
userLogCallback: UserLogCallback,
96-
doneEmitter: EventEmitter
97-
) {
89+
constructor(info: FunctionInfo, request: RpcInvocationRequest, userLogCallback: UserLogCallback) {
9890
this.invocationId = <string>request.invocationId;
9991
this.traceContext = fromRpcTraceContext(request.traceContext);
10092
const executionContext = <ExecutionContext>{
@@ -116,10 +108,6 @@ class InvocationContext implements Context {
116108

117109
this.bindingData = getNormalizedBindingData(request);
118110
this.bindingDefinitions = getBindingDefinitions(info);
119-
120-
this.done = (err?: unknown, result?: any) => {
121-
doneEmitter.emit('done', err, result);
122-
};
123111
}
124112
}
125113

@@ -128,8 +116,6 @@ export interface InvocationResult {
128116
bindings: ContextBindings;
129117
}
130118

131-
export type DoneCallback = (err?: unknown, result?: any) => void;
132-
133119
export type UserLogCallback = (level: RpcLog.Level, ...args: any[]) => void;
134120

135121
export interface Dict<T> {

src/InvocationModel.ts

Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,9 @@ import { format } from 'util';
1414
import { CreateContextAndInputs } from './Context';
1515
import { toTypedData } from './converters/RpcConverters';
1616
import { FunctionInfo } from './FunctionInfo';
17-
import { isError } from './utils/ensureErrorType';
18-
import EventEmitter = require('events');
19-
20-
const asyncDoneLearnMoreLink = 'https://go.microsoft.com/fwlink/?linkid=2097909';
2117

2218
export class InvocationModel implements coreTypes.InvocationModel {
23-
#doneEmitter: EventEmitter = new EventEmitter();
2419
#isDone = false;
25-
#resultIsPromise = false;
2620
#coreCtx: CoreInvocationContext;
2721
#funcInfo: FunctionInfo;
2822

@@ -35,39 +29,14 @@ export class InvocationModel implements coreTypes.InvocationModel {
3529
const { context, inputs } = CreateContextAndInputs(
3630
this.#funcInfo,
3731
this.#coreCtx.request,
38-
(level: RpcLog.Level, ...args: any[]) => this.#userLog(level, ...args),
39-
this.#doneEmitter
32+
(level: RpcLog.Level, ...args: any[]) => this.#userLog(level, ...args)
4033
);
4134
return { context, inputs };
4235
}
4336

4437
async invokeFunction(context: Context, inputs: unknown[], functionCallback: AzureFunction): Promise<unknown> {
45-
const legacyDoneTask = new Promise((resolve, reject) => {
46-
this.#doneEmitter.on('done', (err?: unknown, result?: unknown) => {
47-
this.#onDone();
48-
if (isError(err)) {
49-
reject(err);
50-
} else {
51-
resolve(result);
52-
}
53-
});
54-
});
55-
5638
try {
57-
let rawResult = functionCallback(context, ...inputs);
58-
this.#resultIsPromise = !!rawResult && typeof rawResult.then === 'function';
59-
let resultTask: Promise<any>;
60-
if (this.#resultIsPromise) {
61-
rawResult = Promise.resolve(rawResult).then((r) => {
62-
this.#onDone();
63-
return r;
64-
});
65-
resultTask = Promise.race([rawResult, legacyDoneTask]);
66-
} else {
67-
resultTask = legacyDoneTask;
68-
}
69-
70-
return await resultTask;
39+
return await Promise.resolve(functionCallback(context, ...inputs));
7140
} finally {
7241
this.#isDone = true;
7342
}
@@ -91,7 +60,7 @@ export class InvocationModel implements coreTypes.InvocationModel {
9160
const isDurableBinding = info?.bindings?.name?.type == 'activityTrigger';
9261

9362
const returnBinding = info.getReturnBinding();
94-
// Set results from return / context.done
63+
// Set results from return
9564
if (result || (isDurableBinding && result != null)) {
9665
// $return binding is found: return result data to $return binding
9766
if (returnBinding) {
@@ -153,21 +122,10 @@ export class InvocationModel implements coreTypes.InvocationModel {
153122
#userLog(level: RpcLog.Level, ...args: any[]): void {
154123
if (this.#isDone && this.#coreCtx.state !== 'postInvocationHooks') {
155124
let badAsyncMsg =
156-
"Warning: Unexpected call to 'log' on the context object after function execution has completed. Please check for asynchronous calls that are not awaited or calls to 'done' made before function execution completes. ";
157-
badAsyncMsg += `Function name: ${this.#funcInfo.name}. Invocation Id: ${this.#coreCtx.invocationId}. `;
158-
badAsyncMsg += `Learn more: ${asyncDoneLearnMoreLink}`;
125+
"Warning: Unexpected call to 'log' on the context object after function execution has completed. Please check for asynchronous calls that are not awaited. ";
126+
badAsyncMsg += `Function name: ${this.#funcInfo.name}. Invocation Id: ${this.#coreCtx.invocationId}.`;
159127
this.#systemLog(RpcLog.Level.Warning, badAsyncMsg);
160128
}
161129
this.#log(level, RpcLog.RpcLogCategory.User, ...args);
162130
}
163-
164-
#onDone(): void {
165-
if (this.#isDone) {
166-
const message = this.#resultIsPromise
167-
? `Error: Choose either to return a promise or call 'done'. Do not use both in your script. Learn more: ${asyncDoneLearnMoreLink}`
168-
: "Error: 'done' has already been called. Please check your script for extraneous calls to 'done'.";
169-
this.#systemLog(RpcLog.Level.Error, message);
170-
}
171-
this.#isDone = true;
172-
}
173131
}

src/http/Response.ts

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,6 @@ export class Response implements HttpResponseFull {
1212
enableContentNegotiation?: boolean;
1313
[key: string]: any;
1414

15-
// NOTE: This is considered private and people should not be referencing it, but for the sake of backwards compatibility we will avoid using `#`
16-
_done: Function;
17-
18-
constructor(done: Function) {
19-
this._done = done;
20-
}
21-
22-
end(body?: any) {
23-
if (body !== undefined) {
24-
this.body = body;
25-
}
26-
this.setContentType();
27-
this._done();
28-
return this;
29-
}
30-
3115
setHeader(field: string, val: any): HttpResponseFull {
3216
this.headers[field.toLowerCase()] = val;
3317
return this;
@@ -47,24 +31,10 @@ export class Response implements HttpResponseFull {
4731
return this;
4832
}
4933

50-
sendStatus(statusCode: string | number) {
51-
this.status(statusCode);
52-
// eslint-disable-next-line deprecation/deprecation
53-
return this.end();
54-
}
55-
5634
type(type) {
5735
return this.set(HeaderName.contentType, type);
5836
}
5937

60-
json(body) {
61-
this.type(MediaType.json);
62-
// eslint-disable-next-line deprecation/deprecation
63-
this.send(body);
64-
return;
65-
}
66-
67-
send = this.end;
6838
header = this.setHeader;
6939
set = this.setHeader;
7040
get = this.getHeader;

test/Context.test.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,9 @@ const timerTriggerInput: RpcParameterBinding = {
2525

2626
describe('Context', () => {
2727
let _logger: any;
28-
let doneEmitter: any;
2928

3029
beforeEach(() => {
3130
_logger = sinon.spy();
32-
doneEmitter = sinon.spy();
3331
});
3432

3533
it('camelCases timer trigger input when appropriate', async () => {
@@ -49,7 +47,7 @@ describe('Context', () => {
4947
},
5048
},
5149
});
52-
const workerOutputs = CreateContextAndInputs(info, msg, _logger, doneEmitter);
50+
const workerOutputs = CreateContextAndInputs(info, msg, _logger);
5351
const myTimerWorker = workerOutputs.inputs[0];
5452
expect(myTimerWorker.schedule).to.be.empty;
5553
expect(myTimerWorker.scheduleStatus.last).to.equal('2016-10-04T10:15:00+00:00');
@@ -76,7 +74,7 @@ describe('Context', () => {
7674
},
7775
});
7876

79-
const { context } = CreateContextAndInputs(info, msg, _logger, doneEmitter);
77+
const { context } = CreateContextAndInputs(info, msg, _logger);
8078
expect(context.bindingData.sys).to.be.undefined;
8179
expect(context.bindingData.invocationId).to.equal('1');
8280
expect(context.invocationId).to.equal('1');
@@ -110,7 +108,7 @@ describe('Context', () => {
110108
},
111109
});
112110

113-
const { context } = CreateContextAndInputs(info, msg, _logger, doneEmitter);
111+
const { context } = CreateContextAndInputs(info, msg, _logger);
114112
const { bindingData } = context;
115113
expect(bindingData.sys.methodName).to.equal('test');
116114
expect(bindingData.sys.randGuid).to.not.be.undefined;
@@ -163,7 +161,7 @@ describe('Context', () => {
163161
},
164162
});
165163

166-
const { context } = CreateContextAndInputs(info, msg, _logger, doneEmitter);
164+
const { context } = CreateContextAndInputs(info, msg, _logger);
167165
const { bindingData } = context;
168166
expect(bindingData.invocationId).to.equal('1');
169167
expect(bindingData.headers.header1).to.equal('value1');
@@ -207,7 +205,7 @@ describe('Context', () => {
207205
},
208206
});
209207

210-
const { context } = CreateContextAndInputs(info, msg, _logger, doneEmitter);
208+
const { context } = CreateContextAndInputs(info, msg, _logger);
211209
const { bindingData } = context;
212210
expect(bindingData.invocationId).to.equal('1');
213211
expect(bindingData.headers.header1).to.equal('value1');

types/index.d.ts

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ declare module '@azure/functions' {
3838
[name: string]: any;
3939
}
4040
/**
41-
* The context object can be used for writing logs, reading data from bindings, setting outputs and using
42-
* the context.done callback when your exported function is synchronous. A context object is passed
41+
* The context object can be used for writing logs, reading data from bindings, and setting outputs. A context object is passed
4342
* to your function from the Azure Functions runtime on function invocation.
4443
*/
4544
export interface Context {
@@ -73,19 +72,6 @@ declare module '@azure/functions' {
7372
* at the default trace level.
7473
*/
7574
log: Logger;
76-
/**
77-
* A callback function that signals to the runtime that your code has completed. If your function is synchronous,
78-
* you must call context.done at the end of execution. If your function is asynchronous, you should not use this
79-
* callback.
80-
*
81-
* @param err A user-defined error to pass back to the runtime. If present, your function execution will fail.
82-
* @param result An object containing output binding data. `result` will be passed to JSON.stringify unless it is
83-
* a string, Buffer, ArrayBufferView, or number.
84-
*
85-
* @deprecated Use of sync functions with `context.done()` is not recommended. Use async/await and pass the response as the return value instead.
86-
* See the docs here for more information: https://aka.ms/functions-js-async-await
87-
*/
88-
done(err?: Error | string | null, result?: any): void;
8975
/**
9076
* HTTP request object. Provided to your function when using HTTP Bindings.
9177
*/
@@ -312,34 +298,6 @@ declare module '@azure/functions' {
312298
* @returns the updated HttpResponseFull instance
313299
*/
314300
type(type: string): HttpResponseFull;
315-
/**
316-
* Automatically sets the content-type then calls context.done()
317-
* @returns updated HttpResponseFull instance
318-
* @deprecated this method calls context.done() which is deprecated, use async/await and pass the response as the return value instead.
319-
* See the docs here for more information: https://aka.ms/functions-js-async-await
320-
*/
321-
send(body?: any): HttpResponseFull;
322-
/**
323-
* Same as send()
324-
* Automatically sets the content-type then calls context.done()
325-
* @returns updated HttpResponseFull instance
326-
* @deprecated this method calls context.done() which is deprecated, use async/await and pass the response as your function's return value instead.
327-
* See the docs here for more information: https://aka.ms/functions-js-async-await
328-
*/
329-
end(body?: any): HttpResponseFull;
330-
/**
331-
* Sets the status code then calls send()
332-
* @returns updated HttpResponseFull instance
333-
* @deprecated this method calls context.done() which is deprecated, use async/await and pass the response as your function's return value instead.
334-
* See the docs here for more information: https://aka.ms/functions-js-async-await
335-
*/
336-
sendStatus(statusCode: string | number): HttpResponseFull;
337-
/**
338-
* Sets the 'Content-Type' header to 'application/json' then calls send(body)
339-
* @deprecated this method calls context.done() which is deprecated, use async/await and pass the response as your function's return value instead.
340-
* See the docs here for more information: https://aka.ms/functions-js-async-await
341-
*/
342-
json(body?: any): void;
343301
}
344302
/**
345303
* Http response object.

types/index.test.ts

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,11 @@ export const timerTrigger: AzureFunction = async function (context: Context, myT
4545
context.log('Timer trigger function ran!', timeStamp);
4646
};
4747

48-
const runServiceBus: AzureFunction = function (context: Context, myQueueItem: string) {
48+
const runServiceBus: AzureFunction = async function (context: Context, myQueueItem: string) {
4949
context.log('Node.js ServiceBus queue trigger function processed message', myQueueItem);
5050
context.log.verbose('EnqueuedTimeUtc =', context.bindingData.enqueuedTimeUtc);
5151
context.log.verbose('DeliveryCount =', context.bindingData.deliveryCount);
5252
context.log.verbose('MessageId =', context.bindingData.messageId);
53-
context.done();
5453
};
5554

5655
// Assumes output binding is named '$return'
@@ -148,26 +147,6 @@ const runHttpWithQueue: AzureFunction = async function (context: Context, req: H
148147
return;
149148
};
150149

151-
const returnWithContextDone: AzureFunction = function (context: Context, _req: HttpRequest) {
152-
context.log.info('Writing to queue');
153-
context.done(null, { myOutput: { text: 'hello there, world', noNumber: true } });
154-
};
155-
156-
const returnWithContextDoneMethods: AzureFunction = function (context: Context, _req: HttpRequest) {
157-
context.res = context.res as HttpResponseFull;
158-
context.res.send('hello world');
159-
context.res.end('hello world');
160-
context.res.sendStatus(200);
161-
};
162-
163-
const returnWithJson: AzureFunction = function (context: Context, req: HttpRequest) {
164-
if (context.res?.status instanceof Function) {
165-
context.res.status(200).json({
166-
hello: 'world',
167-
});
168-
}
169-
};
170-
171150
export {
172151
runHttp,
173152
cookieFunction,
@@ -178,9 +157,6 @@ export {
178157
runServiceBus,
179158
runFunction,
180159
runHttpWithQueue,
181-
returnWithContextDone,
182-
returnWithContextDoneMethods,
183-
returnWithJson,
184160
};
185161

186162
// Function returns custom object

0 commit comments

Comments
 (0)