2017-12-22 11 views
1

ノードasync_hooksを使用して非同期スタックを介してコンテキストをトラッキングしようとしています。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()); 
     }); 
    }); 
    }); 
}); 

nextを呼び出すときに作成されたpromiseのtriggerAsyncIdがPromise.resolve()呼び出しのexecutionAsyncIdであるため、このテストケースは失敗します。現在の非同期スタックの外側で作成されたもので、別のコンテキストです。私はそれが作成されたコンテキストとnext機能の非同期コンテキストと結婚する方法を参照することはできません。

https://github.com/domarmstrong/async_hook_then_example

+0

https://github.com/nodejs/help/issues/1036ノードヘルプrepoに問題を作成しました。 – DomA

答えて

0

私は完璧ではないソリューションを、見つけましたが、作業を行います。 Promise.allで元の約束事をラップすると、正しいexecutionAsyncIdが解決されます。しかし、それは約束の文脈を認識している呼び出しコードに依存しています。

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

私はそれを説明するためにblog postnode-request-contextと呼ばれる非常によく似たパッケージを書きました。

fooの値を定義しておらず、キーなしでservice.get()を呼び出すときに値を要求していません。しかし、あなたがその質問を書いたときにはそれは小さな間違いだったと思います。

あなたが名前を付けた主な問題は、場所がPromise.resolveだったことです。私は同意する、それを動作させる方法はない。これは正確にrun関数を作成した理由です。したがって、executionAsyncIdをキャッチし、それを使用してコードを追跡します。それ以外の場合は、コンテキストをトラッキングできませんでした。

あなたのコードは、単にテストのためだったが、あなたが本当に必要がある場合は、矢印の機能を使用してごまかすことができます。

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

もちろん、それは問題ありません。私は明示的に言いませんでしたが、私の主張は本当にあなたのコントロールから外れたコードであり、コンテキスト外でこれを行い、コンテキストトラッキングを完全に壊してしまいます。私は外部ライブラリでこれを行うコードを発見しました。コンテキストが失われた場所でデバッグするのは非常に難しいです。だから、非同期フックは常にあなたのコントロールからコードで動作するとは信じられません。 – DomA

+0

あなたは正しいです。実験的な機能であることに注意してください。 Node GitHubプロジェクトでは、これについて多くの議論があります。状況が変わり、新しいAPIが追加される可能性があります。 –

+0

Ps - あなたが私の答えを気に入ったら、投票してください:) –

関連する問題