Repository: artusjs/core
Pull request #168 (2022-08-11 06:38:51)

fix: lifecycle hook should trigger only one time
<!--
Thank you for your pull request. Please review below requirements.
Bug fixes and new features should include tests and possibly benchmarks.
Contributors guide: https://github.com/eggjs/egg/blob/master/CONTRIBUTING.md

感谢您贡献代码。请确认下列 checklist 的完成情况。
Bug 修复和新功能必须包含测试，必要时请附上性能测试。
Contributors guide: https://github.com/eggjs/egg/blob/master/CONTRIBUTING.md
-->

##### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] `npm test` passes
- [x] tests and/or benchmarks are included
- [ ] documentation is changed or added
- [x] commit message follows commit guidelines

##### Description of change
<!-- Provide a description of the change below this comment. -->

生命周期的 hooks 应该只执行一次。

> 举例：使用 ts-node 跑 artus 应用，ctrl + c 的时候，SIGINT 触发两次会触发两次 `beforeClose`


codecov-commenter: # [Codecov](https://codecov.io/gh/artusjs/core/pull/168?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#168](https://codecov.io/gh/artusjs/core/pull/168?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (789ef01) into [master](https://codecov.io/gh/artusjs/core/commit/5a8d2780c2191696029c62b269b0707cdd33ddfb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (5a8d278) will **increase** coverage by `0.28%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##           master     #168      +/-   ##
==========================================
+ Coverage   89.57%   89.86%   +0.28%     
==========================================
  Files          51       51              
  Lines        1094     1115      +21     
  Branches      180      182       +2     
==========================================
+ Hits          980     1002      +22     
+ Misses        114      113       -1     
```


| [Impacted Files](https://codecov.io/gh/artusjs/core/pull/168?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [src/application.ts](https://codecov.io/gh/artusjs/core/pull/168/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-c3JjL2FwcGxpY2F0aW9uLnRz) | `93.97% <100.00%> (+2.54%)` | :arrow_up: |
| [src/constant.ts](https://codecov.io/gh/artusjs/core/pull/168/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-c3JjL2NvbnN0YW50LnRz) | `100.00% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)

noahziheng: 改动思路或许有点过重了？

而且按照 Spec，生命周期中的 emitHook 其实应该只允许执行一次且必须按照 insert 的顺序执行；

是因为历史问题而没有加校验，是不是应该在 LifecycleManager 里面补全校验逻辑，既能解决问题，也无需引入新概念，反正都是 BREAKING CHANGE
whxaxes: @noahziheng  阔以，我去改一下 LifecycleManager ，state 这个我也不确定是不是合适，先提出来给大家 review
whxaxes: @noahziheng  妥了

Patch:
diff --git a/src/lifecycle/index.ts b/src/lifecycle/index.ts
index e86680d..c83f216 100644
--- a/src/lifecycle/index.ts
+++ b/src/lifecycle/index.ts
@@ -68,6 +68,8 @@ export class LifecycleManager {
     if (!Array.isArray(fnList)) {
       return;
     }
+    // lifecycle hook should only trigger one time
+    this.hookFnMap.delete(hookName);
     for (const hookFn of fnList) {
       await hookFn({
         app: this.app,


Test patch:
diff --git a/test/lifecycle.test.ts b/test/lifecycle.test.ts
index dd64932..735df6f 100644
--- a/test/lifecycle.test.ts
+++ b/test/lifecycle.test.ts
@@ -50,4 +50,42 @@ describe('test/lifecycle.test.ts', () => {
       'app_beforeClose',
     ]);
   });
+
+  it('should not trigger lifecyle multi times', async () => {
+    const appWithLifeCyclePath = path.resolve(__dirname, './fixtures/app_with_lifecycle');
+    const app = await createApp(appWithLifeCyclePath);
+    const lifecycleList = app.container.get(LifecycleList).lifecycleList;
+    await Promise.all([
+      app.run(),
+      app.run(),
+      app.run(),
+    ]);
+
+    await Promise.all([
+      app.close(),
+      app.close(),
+      app.close(),
+    ]);
+
+    assert.deepStrictEqual(lifecycleList, [
+      'pluginA_configWillLoad',
+      'pluginB_configWillLoad',
+      'app_configWillLoad',
+      'pluginA_configDidLoad',
+      'pluginB_configDidLoad',
+      'app_configDidLoad',
+      'pluginA_didLoad',
+      'pluginB_didLoad',
+      'app_didLoad',
+      'pluginA_willReady',
+      'pluginB_willReady',
+      'app_willReady',
+      'pluginA_didReady',
+      'pluginB_didReady',
+      'app_didReady',
+      'pluginA_beforeClose',
+      'pluginB_beforeClose',
+      'app_beforeClose',
+    ]);
+  });
 });