Skip to content
This repository was archived by the owner on Apr 8, 2020. It is now read-only.

Commit 0c02c6d

Browse files
Update domain-task package to version 2.0.1 (major bump because breaking change) and modify 'fetch' behaviour so it no longer tries to register the task with domain-task automatically. See code comments for reasons.
1 parent c1a1bdf commit 0c02c6d

File tree

4 files changed

+34
-8
lines changed

4 files changed

+34
-8
lines changed

src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "aspnet-prerendering",
3-
"version": "1.0.2",
3+
"version": "1.0.4",
44
"description": "Helpers for server-side rendering of JavaScript applications in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.",
55
"main": "index.js",
66
"scripts": {
@@ -10,7 +10,7 @@
1010
"author": "Microsoft",
1111
"license": "Apache-2.0",
1212
"dependencies": {
13-
"domain-task": "^1.0.1",
13+
"domain-task": "^2.0.1",
1414
"es6-promise": "^3.1.2"
1515
},
1616
"devDependencies": {

src/Microsoft.AspNetCore.SpaServices/npm/domain-task/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "domain-task",
3-
"version": "1.0.1",
3+
"version": "2.0.1",
44
"description": "Tracks outstanding operations for a logical thread of execution",
5-
"main": "main.js",
5+
"main": "index.js",
66
"scripts": {
77
"prepublish": "tsd update && tsc && echo 'Finished building NPM package \"domain-task\"'",
88
"test": "echo \"Error: no test specified\" && exit 1"

src/Microsoft.AspNetCore.SpaServices/npm/domain-task/src/fetch.ts

+27-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as url from 'url';
22
import * as domain from 'domain';
33
import * as domainContext from 'domain-context';
4-
import { addTask } from './main';
54
const isomorphicFetch = require('isomorphic-fetch');
65
const isBrowser: boolean = (new Function('try { return this === window; } catch (e) { return false; }'))();
76

@@ -42,9 +41,33 @@ function issueRequest(baseUrl: string, req: string | Request, init?: RequestInit
4241
}
4342

4443
export function fetch(url: string | Request, init?: RequestInit): Promise<any> {
45-
const promise = issueRequest(baseUrl(), url, init);
46-
addTask(promise);
47-
return promise;
44+
// As of domain-task 2.0.0, we no longer auto-add the 'fetch' promise to the current domain task list.
45+
// This is because it's misleading to do so, and can result in race-condition bugs, e.g.,
46+
// https://github.com/aspnet/JavaScriptServices/issues/166
47+
//
48+
// Consider this usage:
49+
//
50+
// import { fetch } from 'domain-task/fetch';
51+
// fetch(something).then(callback1).then(callback2) ...etc... .then(data => updateCriticalAppState);
52+
//
53+
// If we auto-add the very first 'fetch' promise to the domain task list, then the domain task completion
54+
// callback might fire at any point among all the chained callbacks. If there are enough chained callbacks,
55+
// it's likely to occur before the final 'updateCriticalAppState' one. Previously we thought it was enough
56+
// for domain-task to use setTimeout(..., 0) so that its action occurred after all synchronously-scheduled
57+
// chained promise callbacks, but this turns out not to be the case. Current versions of Node will run
58+
// setTimeout-scheduled callbacks *before* setImmediate ones, if their timeout has elapsed. So even if you
59+
// use setTimeout(..., 10), then this callback will run before setImmediate(...) if there were 10ms or more
60+
// of CPU-blocking activity. In other words, a race condition.
61+
//
62+
// The correct design is for the final chained promise to be the thing added to the domain task list, but
63+
// this can only be done by the developer and not baked into the 'fetch' API. The developer needs to write
64+
// something like:
65+
//
66+
// var myTask = fetch(something).then(callback1).then(callback2) ...etc... .then(data => updateCriticalAppState);
67+
// addDomainTask(myTask);
68+
//
69+
// ... so that the domain-tasks-completed callback never fires until after 'updateCriticalAppState'.
70+
return issueRequest(baseUrl(), url, init);
4871
}
4972

5073
export function baseUrl(url?: string): string {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// This file determines the top-level package exports
2+
export { addTask, run } from './main';
3+
export { fetch } from './fetch';

0 commit comments

Comments
 (0)