Closed sangheon-kim closed 3 years ago
AsyncLib.ts
class AsyncLib { state: Object | { [key: string]: any }; constructor() { this.state = {}; this.constant = this.constant.bind(this); this.shWaterFall = this.shWaterFall.bind(this); } /** * * @description 들어오는 값으로 상태 변경 * @param {{ [key: string]: any }} params * @returns * @author Sangheon Kim * @memberof AsyncLib */ constant(params: { [key: string]: any }) { return new Promise((resolve, reject) => { if (Object.keys.length > 0) { // params로 사용할 객체가 넘어왔다면 인스턴스의 state를 초기화 해주고, // success Resolve 해준다. this.state = params; resolve("Success"); } else { // 파라미터로 온것이 객체타입이 아니거나, 전달받은게 없는 경우에 reject 해준다. reject("No params"); } }); } /** * * @description 순차적으로 들어온 콜백 리스트들을 실행 후 세번째 매개변수로 넘어온 callback 함수를 실행시켜주어 결과 반환 * @param {{ [key: string]: any }} initial * @param {Array<(params?: any) => any>} callbackArray * @param {*} [cb] (err, result) 이렇게 두가지의 매개변수를 전달받을 콜백을 넣어주면된다. * @memberof AsyncLib */ shWaterFall( initial: { [key: string]: any }, callbackArray: Array<(params?: any) => any>, cb?: any ) { /** * @description 콜백 함수 리스트를 전달받아서 순차적으로 실행 */ const g: Generator = function* (this: AsyncLib) { this.constant(initial); // shWaterfall의 첫번째 인수로 전달받은 초기값을 기반으로 state초기화 for (let callback of callbackArray) { // callbackArray는 결국 Array 형태이기에 이터레이션 프로토콜을 준수하고 있기에 for...of 사용 가능 yield (async ( genObj: Generator, promise: (params?: { [key: string]: any }) => Promise<any> ) => { // 첫번째 매개변수로는 자기자신 제너레이터를 전달받고, 두번째 매개변수로 promise 함수를 전달받는다. callbackArray가 끝나면 결국 이터레이터는 종료 try { // promise 결과값을 result 변수에 할당 const result = await promise(this.state); // 결과값을 인스턴스의 state에 할당해준다. this.state = result; // 그리고 결과값을 다음 콜백함수의 파라미터로 넘겨준다. genObj.next(result); } catch (err) { console.log("errorZZz", err); // 에러시에는 첫번째 인수에 err를 전달하여 에러에 대한 응답 반환을 처리한다. return cb(err, null); } })(g, callback); // IIFE이기에 매개변수를 직접 내가 할당해주었다. 제너레이터함수는 호출하면 제너레이터를 반환 } // 에러없이 정상적으로 실행되었을 경우에 현재 파라미터에는 모든 콜백을 돌고돌아 오버라이딩되고 추가된 파라미터들이 담긴 결과 object가 있을 것이다. // 이걸 전달받은 콜백함수의 두번째 인수로 전달해준다. return cb(null, this.state); }.call(new AsyncLib()); // this에 AsyncLib을 넣어주어 State 참조하게 만든다. // 초기에는 한번 정적으로 실행해준 코드를 넣어주었다. 어짜피 처음 제너레이터 오브젝트의 next메소드에는 아무것도 전달할 수 없다. g.next(); } } // 생성자 함수를 사용하여 instance를 내보내준다. export default new AsyncLib();
Usage Example
import { Request, Response } from "express"; import jwt from "jsonwebtoken"; import { jwtSecret } from "../app"; import AuthService from "../services/AuthService2"; import ValidationParams from "../utils/validationParams"; import { makeSucessedResponse, makeErrorResponse } from "../utils"; import async from "../utils/asyncLib"; class AuthController { constructor() { this.join = this.join.bind(this); } /** * * @description 로그인 관련 컨트롤러 * @date 2020-12-11 * @param {Request} req * @param {Response} res * @memberof AuthController */ async login(req: Request, res: Response) { const makeResponse = (err: null | Error, data: null | any) => { if (err) { makeErrorResponse({ res, err }); return; } const token = jwt.sign({ sub: data.id }, jwtSecret); const params = { ...data["result"], accessToken: token, }; makeSucessedResponse({ res, data: params, cookie: { accessToken: token, option: { maxAge: 90000, httpOnly: true } }, }); }; async.shWaterFall( req.body, [ ValidationParams.validParams, ValidationParams.validEmail, AuthService.hashPassword, AuthService.matchUser, ], makeResponse ); } /** * * @description 회원가입 관련 컨트롤러 * @date 2020-12-09 * @author Sangheon Kim * @param {Request} req * @param {Response} res * @memberof AuthController */ async join(req: Request, res: Response) { const makeResponse = (err: null | Error, __: any) => { if (err) { makeErrorResponse({ res, err }); return; } makeSucessedResponse({ res }); }; async.shWaterFall( req.body, [ ValidationParams.validParams, ValidationParams.validEmail, AuthService.emailDuplicateCheck, AuthService.hashPassword, AuthService.createUser, ], makeResponse ); } } export default new AuthController();
깔끔하게 정리 완료!
AsyncLib.ts
Usage Example