Ex 10
๋ฌธ์
/*
Intro:
We have asynchronous functions now, advanced technology.
This makes us a tech startup officially now.
But one of the consultants spoiled our dreams about
inevitable future IT leadership.
He said that callback-based asynchronicity is not
popular anymore and everyone should use Promises.
He promised that if we switch to Promises, this would
bring promising results.
Exercise:
We don't want to reimplement all the data-requesting
functions. Let's decorate the old callback-based
functions with the new Promise-compatible result.
The final function should return a Promise which
would resolve with the final data directly
(i.e. users or admins) or would reject with an error
(or type Error).
The function should be named promisify.
Higher difficulty bonus exercise:
Create a function promisifyAll which accepts an object
with functions and returns a new object where each of
the function is promisified.
Rewrite api creation accordingly:
const api = promisifyAll(oldApi);
*/
interface User {
type: 'user';
name: string;
age: number;
occupation: string;
}
interface Admin {
type: 'admin';
name: string;
age: number;
role: string;
}
type Person = User | Admin;
const admins: Admin[] = [
{ type: 'admin', name: 'Jane Doe', age: 32, role: 'Administrator' },
{ type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' }
];
const users: User[] = [
{ type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
{ type: 'user', name: 'Kate Mรผller', age: 23, occupation: 'Astronaut' }
];
export type ApiResponse<T> = (
{
status: 'success';
data: T;
} |
{
status: 'error';
error: string;
}
);
export function promisify(arg: unknown): unknown {
return null;
}
const oldApi = {
requestAdmins(callback: (response: ApiResponse<Admin[]>) => void) {
callback({
status: 'success',
data: admins
});
},
requestUsers(callback: (response: ApiResponse<User[]>) => void) {
callback({
status: 'success',
data: users
});
},
requestCurrentServerTime(callback: (response: ApiResponse<number>) => void) {
callback({
status: 'success',
data: Date.now()
});
},
requestCoffeeMachineQueueLength(callback: (response: ApiResponse<number>) => void) {
callback({
status: 'error',
error: 'Numeric value has exceeded Number.MAX_SAFE_INTEGER.'
});
}
};
export const api = {
requestAdmins: promisify(oldApi.requestAdmins),
requestUsers: promisify(oldApi.requestUsers),
requestCurrentServerTime: promisify(oldApi.requestCurrentServerTime),
requestCoffeeMachineQueueLength: promisify(oldApi.requestCoffeeMachineQueueLength)
};
function logPerson(person: Person) {
console.log(
` - ${person.name}, ${person.age}, ${person.type === 'admin' ? person.role : person.occupation}`
);
}
async function startTheApp() {
console.log('Admins:');
(await api.requestAdmins()).forEach(logPerson);
console.log();
console.log('Users:');
(await api.requestUsers()).forEach(logPerson);
console.log();
console.log('Server time:');
console.log(` ${new Date(await api.requestCurrentServerTime()).toLocaleString()}`);
console.log();
console.log('Coffee machine queue length:');
console.log(` ${await api.requestCoffeeMachineQueueLength()}`);
}
startTheApp().then(
() => {
console.log('Success!');
},
(e: Error) => {
console.log(`Error: "${e.message}", but it's fine, sometimes errors are inevitable.`);
}
);
// In case you are stuck:
// https://www.typescriptlang.org/docs/handbook/2/generics.html
ํ์ด
api ๊ฐ์ฒด๋ฅผ ๋ณด๋ฉด 4๊ฐ์ ์์ฑ๋ค์ด promisify์ ๊ฒฐ๊ณผ๊ฐ์ ์๋ฏธํ๊ณ ์๋ค. ๋ฐ๋ผ์ oldApi๋ฅผ ํ ๊ณณ์์ ๊ด๋ฆฌํ ํจ์ promisify์ ํ์ ์ ์ง์ ํด์ผํ๋ค.
๋จผ์ , ๋งค๊ฐ๋ณ์์ ๊ฐ๊ณผ ๋ฆฌํด๊ฐ์ ํ์ ์ด ๊ฐ๊ฐ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ์ ๋ค๋ฆญ์ ์ฌ์ฉํด ํ์ ์ ์ง์ ํด์ค๋ค. ๋ฆฌํด๊ฐ์ ๊ฒฝ์ฐ, ๋ฌธ์ ์ ์ ํ ๋๋ก Promise ๋ฅผ ์ถ๋ ฅํ๊ฒ๋ ์ง์ ํ๋ค.
export function promisify<T>(arg: unknown): () => Promise<T> {
return () => new Promise((resolve, reject) => {
// resolve ์ผ๋
// else reject ์ผ๋
})
}
๊ทธ๋ฆฌ๊ณ api ์์ promisify ํจ์์ ์ ๋ค๋ฆญ ํ์ ์ ์ถ๊ฐํด์ฃผ๋ฉด ๋ฌธ์ ๋ ํด๊ฒฐ๋๋ค.
export const api = {
requestAdmins: promisify<Admin[]>(oldApi.requestAdmins),
requestUsers: promisify<User[]>(oldApi.requestUsers),
requestCurrentServerTime: promisify<number>(oldApi.requestCurrentServerTime),
requestCoffeeMachineQueueLength: promisify<number>(oldApi.requestCoffeeMachineQueueLength)
};
+
๋ณด๋์ค ๋ฌธ์ ์ธ promisifyAll ๊ตฌํ์ ๋์ค์ ๋ค์ ์๋ํด๋ณด์
type CallbackBasedAsyncFunction<T> = (callback: (response: ApiResponse<T>) => void) => void;
type PromiseBasedAsyncFunction<T> = () => Promise<T>;
export function promisify<T>(fn: CallbackBasedAsyncFunction<T>): PromiseBasedAsyncFunction<T> {
return () => new Promise<T>((resolve, reject) => {
fn((response) => {
if (response.status === 'success') {
resolve(response.data);
} else {
reject(new Error(response.error));
}
});
});
}
type SourceObject<T> = {[K in keyof T]: CallbackBasedAsyncFunction<T[K]>};
type PromisifiedObject<T> = {[K in keyof T]: PromiseBasedAsyncFunction<T[K]>};
export function promisifyAll<T extends {[key: string]: any}>(obj: SourceObject<T>): PromisifiedObject<T> {
const result: Partial<PromisifiedObject<T>> = {};
for (const key of Object.keys(obj) as (keyof T)[]) {
result[key] = promisify(obj[key]);
}
return result as PromisifiedObject<T>;
}
Last updated
Was this helpful?