2017-12-22 2 views
1

Ich versuche, Kontext über den Async-Stack mit Knoten async_hooks zu verfolgen. Es funktioniert in den meisten Fällen jedoch habe ich diesen Anwendungsfall gefunden, dass ich nicht denken kann, wie zu beheben:Tracking-Kontext mit async_hooks

service.js:

const asyncHooks = require('async_hooks'); 

class Service { 
    constructor() { 
    this.store = {}; 
    this.hooks = asyncHooks.createHook({ 
     init: (asyncId, type, triggerAsyncId) => { 
     if (this.store[triggerAsyncId]) { 
      this.store[asyncId] = this.store[triggerAsyncId]; 
     } 
     }, 
     destroy: (asyncId) => { 
     delete this.store[asyncId]; 
     }, 
    }); 
    this.enable(); 
    } 

    async run(fn) { 
    this.store[asyncHooks.executionAsyncId()] = {}; 
    await fn(); 
    } 

    set(key, value) { 
    this.store[asyncHooks.executionAsyncId()][key] = value; 
    } 

    get(key) { 
    const state = this.store[asyncHooks.executionAsyncId()]; 
    if (state) { 
     return state[key]; 
    } else { 
     return null; 
    } 
    } 

    enable() { 
    this.hooks.enable(); 
    } 

    disable() { 
    this.hooks.disable(); 
    } 
} 

module.exports = Service; 

service.spec.js

const assert = require('assert'); 
const Service = require('./service'); 

describe('Service',() => { 
    let service; 

    afterEach(() => { 
    service.disable(); 
    }); 

    it('can handle promises created out of the execution stack', async() => { 
    service = new Service(); 

    const p = Promise.resolve(); 

    await service.run(async() => { 
     service.set('foo'); 

     await p.then(() => { 
     assert.strictEqual('foo', service.get()); 
     }); 
    }); 
    }); 
}); 

Dieser Testfall schlägt fehl, da die triggerAsyncId des beim Aufruf von next erstellten Versprechens die executionAsyncId des Promise.resolve() - Aufrufs ist. Das wurde außerhalb des aktuellen asynchronen Stapels erstellt und ist ein separater Kontext. Ich kann sehen, keiner Weise die next Funktionen async Zusammenhang mit dem Kontext zu heiraten es erstellt wurde.

https://github.com/domarmstrong/async_hook_then_example

+0

https://github.com/nodejs/help/issues/1036 Ein Problem bei Node help Repo wurde erstellt. – DomA

Antwort

0

ich eine Lösung gefunden, die nicht perfekt ist, aber funktioniert. Wenn Sie das ursprüngliche Versprechen mit Promise.all übernehmen, wird die korrekte AusführungAsyncId ausgeführt. Es beruht jedoch darauf, dass der aufrufende Code den Kontext der Versprechen kennt.

const assert = require('assert'); 
const Service = require('./service'); 

describe('Service',() => { 
    let service; 

    afterEach(() => { 
    service.disable(); 
    }); 

    it('can handle promises created out of the execution stack', async() => { 
    service = new Service(); 

    const p = Promise.resolve(); 

    await service.run(async() => { 
     service.set('foo'); 

     await Promise.all([p]).then(() => { 
     assert.strictEqual('foo', service.get()); 
     }); 
    }); 
    }); 
}); 
1

Ich schrieb ein sehr ähnliches Paket node-request-context mit einem blog post genannt, es zu erklären.

Sie haben keinen Wert für foo definiert und Sie fragen keinen Wert, wenn Sie service.get() ohne Schlüssel aufrufen. Aber ich denke, das war ein kleiner Fehler beim Schreiben der Frage.

Das Hauptproblem, das Sie genannt haben, war der Standort Promise.resolve. Ich stimme zu, es gibt keine Möglichkeit, es zum Laufen zu bringen. Dies ist genau der Grund, warum Sie die run Funktion erstellt haben, so dass Sie die executionAsyncId fangen und Ihren Code damit verfolgen können. Andernfalls konnten Sie keinen Kontext verfolgen.

Der Code war nur zum Testen, aber wenn Sie wirklich brauchen, können Sie betrügen durch den Pfeil Funktion:

it('can handle promises created out of the execution stack', async() => { 
    service = new Service(); 

    const p =() => Promise.resolve(); 

    await service.run(async() => { 


    service.set('foo', 'bar'); 

    await p().then(() => { 
     assert.strictEqual('bar', service.get('foo')); 
    }); 
    }); 
}); 
+0

Sicher, das ist in Ordnung. Ich habe nicht explizit gesagt, aber mein Punkt ist wirklich, dass der Code außerhalb Ihrer Kontrolle, außerhalb des Kontexts, dies tun kann und das Context-Tracking vollständig durchbricht. Ich habe Code gefunden, der dies in externen Bibliotheken tut. Und es ist sehr schwierig zu debuggen, wo der Kontext verloren geht. Sie können also nicht darauf vertrauen, dass asynchrone Hooks immer mit Code arbeiten, den Sie nicht kontrollieren können. – DomA

+0

Sie haben Recht. Beachten Sie, dass es sich um eine experimentelle Funktion handelt. Es gibt eine Menge Diskussion darüber auf Node GitHub Projekt. Dinge können sich ändern und neue API kann hinzugefügt werden –

+0

Ps - wenn Sie meine Antwort mochte, bitte stimmen Sie :) –