{"action":"created","issue":{"url":"https://api.github.com/repos/caolan/async/issues/999","labels_url":"https://api.github.com/repos/caolan/async/issues/999/labels{/name}","comments_url":"https://api.github.com/repos/caolan/async/issues/999/comments","events_url":"https://api.github.com/repos/caolan/async/issues/999/events","html_url":"https://github.com/caolan/async/pull/999","id":124830850,"number":999,"title":"Memoize causes queued callbacks to jump across domains","user":{"login":"owenallenaz","id":1087603,"avatar_url":"https://avatars.githubusercontent.com/u/1087603?v=3","gravatar_id":"","url":"https://api.github.com/users/owenallenaz","html_url":"https://github.com/owenallenaz","followers_url":"https://api.github.com/users/owenallenaz/followers","following_url":"https://api.github.com/users/owenallenaz/following{/other_user}","gists_url":"https://api.github.com/users/owenallenaz/gists{/gist_id}","starred_url":"https://api.github.com/users/owenallenaz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/owenallenaz/subscriptions","organizations_url":"https://api.github.com/users/owenallenaz/orgs","repos_url":"https://api.github.com/users/owenallenaz/repos","events_url":"https://api.github.com/users/owenallenaz/events{/privacy}","received_events_url":"https://api.github.com/users/owenallenaz/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2016-01-04T20:21:17Z","updated_at":"2016-01-05T18:28:47Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/caolan/async/pulls/999","html_url":"https://github.com/caolan/async/pull/999","diff_url":"https://github.com/caolan/async/pull/999.diff","patch_url":"https://github.com/caolan/async/pull/999.patch"},"body":"Async.memoize queues up callbacks to prevent a cache-stampede. The result of this is that if you call the same function two times before it's completed, then the second callback will actually be executed in the context of the first. This can cause it to jump across domains because the 2nd callback was originally in the context of another domain, but because it was queued, is now put into the context of the 1st domain. This is solved almost identically to the way that the node mongodb driver handles this same problem. I have added a test case for the scenario."},"comment":{"url":"https://api.github.com/repos/caolan/async/issues/comments/169089650","html_url":"https://github.com/caolan/async/pull/999#issuecomment-169089650","issue_url":"https://api.github.com/repos/caolan/async/issues/999","id":169089650,"user":{"login":"owenallenaz","id":1087603,"avatar_url":"https://avatars.githubusercontent.com/u/1087603?v=3","gravatar_id":"","url":"https://api.github.com/users/owenallenaz","html_url":"https://github.com/owenallenaz","followers_url":"https://api.github.com/users/owenallenaz/followers","following_url":"https://api.github.com/users/owenallenaz/following{/other_user}","gists_url":"https://api.github.com/users/owenallenaz/gists{/gist_id}","starred_url":"https://api.github.com/users/owenallenaz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/owenallenaz/subscriptions","organizations_url":"https://api.github.com/users/owenallenaz/orgs","repos_url":"https://api.github.com/users/owenallenaz/repos","events_url":"https://api.github.com/users/owenallenaz/events{/privacy}","received_events_url":"https://api.github.com/users/owenallenaz/received_events","type":"User","site_admin":false},"created_at":"2016-01-05T18:28:47Z","updated_at":"2016-01-05T18:28:47Z","body":"You can, but it's an odd abstraction. Here would be my counter arguments:\r\n\r\n1. It forces application developers to know the inner workings of all of their libraries that way they have to bind a callback that goes through any library that may use memoize internally. Basically any time a library changes global state, it's a problem, and dumping a domain because we use async fits into the same realm I believe.\r\n\r\n2. The deprecation is a bit odd honestly, as we see here https://github.com/nodejs/node/issues/6 in the later comments by Trev Norris, domains won't be removed until there is a replacement. I've searched for a bit but honestly at this moment I haven't found any replacement proposal which is gaining any traction. Basically some node core members hate domains because they believe thrown errors should crash process, but everyone is resigned to the fact that there needs to be some way to handle async error management.\r\n\r\n3. The patch I've submitted is backward and forward compatible. In the event process.domain doesn't exist, be it browser or maybe even a newer version of node which replaces them, the code will continue to function properly.\r\n\r\n4. The change is only like 3 or 4 lines of code. In the event Node does finally replace domains, the likely replacement can't be many more lines of code.\r\n\r\n5. This is a common change required in basically every library that does callback queueing. Most commonly this is done in database drivers (mongodb, mysql) because they utilizes a connection pool which creates a callback array. It's the same fundamental problem as asyncs memoize.\r\n\r\n6. I think your argument would apply if we were binding each async.series or async.parallel into a new domain and utilizing the domain as a form of error management. This branch simply causes it to pass along the domain if the calling application happened to be using one, in this way we aren't altering global state, were simply maintaining it.\r\n\r\nIt's not a deal breaker, we can work around it, but I believe it's the right thing to do. If you want to sit on it for a while and see if other people upvote, that a possibility."}}