如何让每一个服务都等待它需要的服务就绪?
How to make every service wait for the services it needs to be ready?
我正在努力获取等待相关服务准备其数据的编码。我现在有一个脑锁,我没有得到我期望的行为。
我的期望是 home.ts 页面依赖于 B 服务,而 B 服务又依赖于首先完成的 A 服务。此外,我不想让 home.ts 页面知道 A 服务(理想情况下)。
我在 stackblitz 中进行了以下简化尝试,第一次使用每个服务公开的 Ready_P 承诺。但是,我现在意识到,通常取决于注入机制,它是未定义的,因此行 this.BService.Ready_P.then().. 在 home.ts.
的构造函数中失败
better/correct 处理此问题的方法是什么?
// home.ts
import { CService } from './CService'; // 20220122
import { BService } from './BService'; // 20220122
//
@Component({
selector: 'home',
templateUrl: 'home.html',
styleUrls: ['./home.scss'],
// encapsulation: ViewEncapsulation.None
})
export class home {
constructor(
//
//
public CService: CService,
public BService: BService
) {
this.Init();
}
//
async Init() {
await this.CService.Load();
console.log('CService Ready from home');
await this.BService.Load();
console.log('BService Ready from home');
}
}
// C.ts
import { BService } from './BService'; // 20220122
/** # CService Depends on BService being data ready
* - 20220122 */
@Injectable()
export class CService {
// 20220122
constructor(
//
public BService: BService
) {
// this.BService.Load().then(() => {
// console.log('BService Ready from C');
// this.Load();
// });
}
//
//
Ready_P: Promise<boolean>;
//
async Load() {
await this.BService.Load()
// Do work
// if (this.Ready_P) return this.Ready_P; // to avoid repeated execution.
return (this.Ready_P = new Promise((resolve) => {
setTimeout(() => {
console.log('C Resolves');
resolve(true);
}, 1000);
}));
}
}
// B.ts
import { AService } from './AService'; // 20220122
/** # BService Depends on AService being data ready
* - 20220122 */
@Injectable()
export class BService {
// 20220122
constructor(
//
public AService: AService
) {
}
//
//
Ready_P: Promise<boolean>;
//
async Load() {
await this.AService.Load()
// Do work
// if (this.Ready_P) return this.Ready_P // to avoid repeated execution.
return (this.Ready_P = new Promise((resolve) => {
setTimeout(() => {
console.log('B Resolves');
resolve(true);
}, 2000);
}));
}
}
// A.ts is similar to C
完整代码可见here.
注意:我希望我的 A、B、C 服务使用相同的模式进行理想编码。服务可能会或可能不会使用 http.get... 或类似的方法来完成和准备一些数据。为简单起见,我使用 setTimeout() 来模拟它们将花费一些不同的时间来完成。
预期的执行顺序
1. A Resolves
2. AService Ready from B
3. B Resolves
4. BService Ready from C
5. C Resolves
6. CService Ready from home
7. BService Ready from home
我不建议为此目的使用 Promises。相反,Angular 有更好的方法来处理依赖于其他代码才能完成的异步代码。
使用你的代码,我会这样写:
服务A:
@Injectable()
export class AService {
// this service demonstrates dependance on http - an action that takes time to finish
constructor(private http: HttpClient) {}
public load() {
return this.http.get('https://jsonplaceholder.typicode.com/todos');
}
}
注意:出于演示的目的,我使此服务依赖于 http - 一个需要时间才能完成的操作。
服务 B:
@Injectable()
export class BService {
private dataStoreExample: Subject<any> = new Subject<any>();
constructor(private AService: AService) {}
get dataFromServiceB() {
return this.dataStoreExample.asObservable();
}
public load() {
this.AService.load().subscribe((data) => {
console.log('A Resolves');
this.dataStoreExample.next(data);
});
}
}
服务 B 依赖于服务 A,并且仅当服务 A 完成其工作时才发出一个值。 home.ts 反过来只有当 B 发出它时才会收到该值(同样,在 A 完成其工作之后)。
home.ts:
export class home {
constructor(
public Events: Events,
public BService: BService
) {
this.BService.dataFromServiceB.subscribe(
(data) => console.log('BService Ready from home, got data:', data),
(error) => console.log('BService FAIL from home, error:', error)
);
}
}
看这里:https://stackblitz.com/edit/ionic-yvza2i?file=pages/home/home.ts
你走在正确的轨道上。你看到重复项的原因是 home 调用 B 和 C 的 init,B 初始化 A,C 初始化 B,所以你最终得到
home -> B -> A
home -> C -> B -> A
注意 A 和 B 将被初始化两次。如果 classes 确实有这种依赖性,那么将 side-effect 添加到 init 进程中 class 知道它已经完成。类似于:
asnyc Load() {
if (this.wasLoaded) return Promise.resolve();
await this.otherService.Load();
this.wasLoaded = true;
}
一个“side-effect”负载可能是正在设置对象上的某些实例数据。该数据的存在可能是 Load 是否需要执行的一个很好的标志。
另一件要解决的事情是,您已经成功地从服务 A、B、C 的构造函数中删除了异步调用,但是您的 home
class 调用了它的(异步)Load()
构造函数中的方法。把它也去掉。
在文体上,最现代的语法出现在您的 home
class 中。这是一种风格,但我会将该语法(将使用 await
的方法标记为 async
并使用 await
而不是 then()
)复制到您的另一个 classes.
最后,在 async/await
风格中,错误被 try/catch
块捕获,如...
// in class A
async Load() {
if (this.dataWeGetFromB) return Promise.resolve();
try {
this.dataWeGetFromB = await this.BService.Load();
} catch (err) {
// handle err
}
}
我正在努力获取等待相关服务准备其数据的编码。我现在有一个脑锁,我没有得到我期望的行为。 我的期望是 home.ts 页面依赖于 B 服务,而 B 服务又依赖于首先完成的 A 服务。此外,我不想让 home.ts 页面知道 A 服务(理想情况下)。
我在 stackblitz 中进行了以下简化尝试,第一次使用每个服务公开的 Ready_P 承诺。但是,我现在意识到,通常取决于注入机制,它是未定义的,因此行 this.BService.Ready_P.then().. 在 home.ts.
的构造函数中失败better/correct 处理此问题的方法是什么?
// home.ts
import { CService } from './CService'; // 20220122
import { BService } from './BService'; // 20220122
//
@Component({
selector: 'home',
templateUrl: 'home.html',
styleUrls: ['./home.scss'],
// encapsulation: ViewEncapsulation.None
})
export class home {
constructor(
//
//
public CService: CService,
public BService: BService
) {
this.Init();
}
//
async Init() {
await this.CService.Load();
console.log('CService Ready from home');
await this.BService.Load();
console.log('BService Ready from home');
}
}
// C.ts
import { BService } from './BService'; // 20220122
/** # CService Depends on BService being data ready
* - 20220122 */
@Injectable()
export class CService {
// 20220122
constructor(
//
public BService: BService
) {
// this.BService.Load().then(() => {
// console.log('BService Ready from C');
// this.Load();
// });
}
//
//
Ready_P: Promise<boolean>;
//
async Load() {
await this.BService.Load()
// Do work
// if (this.Ready_P) return this.Ready_P; // to avoid repeated execution.
return (this.Ready_P = new Promise((resolve) => {
setTimeout(() => {
console.log('C Resolves');
resolve(true);
}, 1000);
}));
}
}
// B.ts
import { AService } from './AService'; // 20220122
/** # BService Depends on AService being data ready
* - 20220122 */
@Injectable()
export class BService {
// 20220122
constructor(
//
public AService: AService
) {
}
//
//
Ready_P: Promise<boolean>;
//
async Load() {
await this.AService.Load()
// Do work
// if (this.Ready_P) return this.Ready_P // to avoid repeated execution.
return (this.Ready_P = new Promise((resolve) => {
setTimeout(() => {
console.log('B Resolves');
resolve(true);
}, 2000);
}));
}
}
// A.ts is similar to C
完整代码可见here.
注意:我希望我的 A、B、C 服务使用相同的模式进行理想编码。服务可能会或可能不会使用 http.get... 或类似的方法来完成和准备一些数据。为简单起见,我使用 setTimeout() 来模拟它们将花费一些不同的时间来完成。
预期的执行顺序
1. A Resolves
2. AService Ready from B
3. B Resolves
4. BService Ready from C
5. C Resolves
6. CService Ready from home
7. BService Ready from home
我不建议为此目的使用 Promises。相反,Angular 有更好的方法来处理依赖于其他代码才能完成的异步代码。
使用你的代码,我会这样写:
服务A:
@Injectable()
export class AService {
// this service demonstrates dependance on http - an action that takes time to finish
constructor(private http: HttpClient) {}
public load() {
return this.http.get('https://jsonplaceholder.typicode.com/todos');
}
}
注意:出于演示的目的,我使此服务依赖于 http - 一个需要时间才能完成的操作。
服务 B:
@Injectable()
export class BService {
private dataStoreExample: Subject<any> = new Subject<any>();
constructor(private AService: AService) {}
get dataFromServiceB() {
return this.dataStoreExample.asObservable();
}
public load() {
this.AService.load().subscribe((data) => {
console.log('A Resolves');
this.dataStoreExample.next(data);
});
}
}
服务 B 依赖于服务 A,并且仅当服务 A 完成其工作时才发出一个值。 home.ts 反过来只有当 B 发出它时才会收到该值(同样,在 A 完成其工作之后)。
home.ts:
export class home {
constructor(
public Events: Events,
public BService: BService
) {
this.BService.dataFromServiceB.subscribe(
(data) => console.log('BService Ready from home, got data:', data),
(error) => console.log('BService FAIL from home, error:', error)
);
}
}
看这里:https://stackblitz.com/edit/ionic-yvza2i?file=pages/home/home.ts
你走在正确的轨道上。你看到重复项的原因是 home 调用 B 和 C 的 init,B 初始化 A,C 初始化 B,所以你最终得到
home -> B -> A
home -> C -> B -> A
注意 A 和 B 将被初始化两次。如果 classes 确实有这种依赖性,那么将 side-effect 添加到 init 进程中 class 知道它已经完成。类似于:
asnyc Load() {
if (this.wasLoaded) return Promise.resolve();
await this.otherService.Load();
this.wasLoaded = true;
}
一个“side-effect”负载可能是正在设置对象上的某些实例数据。该数据的存在可能是 Load 是否需要执行的一个很好的标志。
另一件要解决的事情是,您已经成功地从服务 A、B、C 的构造函数中删除了异步调用,但是您的 home
class 调用了它的(异步)Load()
构造函数中的方法。把它也去掉。
在文体上,最现代的语法出现在您的 home
class 中。这是一种风格,但我会将该语法(将使用 await
的方法标记为 async
并使用 await
而不是 then()
)复制到您的另一个 classes.
最后,在 async/await
风格中,错误被 try/catch
块捕获,如...
// in class A
async Load() {
if (this.dataWeGetFromB) return Promise.resolve();
try {
this.dataWeGetFromB = await this.BService.Load();
} catch (err) {
// handle err
}
}