Zenn
Open8

JavaScript小片

lndexlndex

引数のプロミスを関数内で展開する。

プロミスに対応した自乗関数
const square = (valueOrPromise) => {
    if (typeof valueOrPromise?.then === 'function') {
        return valueOrPromise.then(square);
    }
    const value = valueOrPromise;
    return value * value;
}
lndexlndex

Promise.withResolvers() もどき。

Promise.withResolvers もどき
const withResolvers = () => {
    let resolve, reject;
    const promise = new Promise((resolve_, reject_) => {
        resolve = resolve_;
        reject  = reject_;
    });
    return { promise, resolve, reject };
};

使用例。

一定期間の待ちを発生させる関数
const wait = (timeout) => {
    const { promise, resolve } = withResolvers();
    setTimeout(resolve, timeout);
    return promise;
};
lndexlndex

タイムアウト

既存の非同期関数にタイムアウトを追加する
const withTimeout = (timeout, asyncTask) => {
    return (...args) => {
        const { promise, reject } = Promise.withResolvers();
        setTimeout(() => reject(new Error('timed out')), timeout);
        return Promise.race([asyncTask(...args), promise]);
    };
};
lndexlndex

フィルタ関数

フィルタ関数
const filter = function *(iterable, f) {
    for (const value of iterable) {
        if (f(value)) yield value;
    }
};
lndexlndex

オーバーライドの確認

オーバーライドの確認
const isOverridden = (o, key) => {
    if (o == null || typeof o !== 'object') { return false; }

    let has = false;
    for (let p = o; p != null; p = Object.getPrototypeOf(p)) {
        if (!Object.hasOwn(p, key)) { continue; }
        if (!has) {
            has = true;
        } else {
            return true;
        }
    }

    return false;
}; 
lndexlndex

汎用のプロパティ記述子取得関数

汎用のプロパティ記述子取得関数
const getPropertyDescriptor = (o, key) => {
    if (o == null || typeof o !== 'object') { return {}; }

    const k = typeof key === 'string' || typeof key === 'symbol' ? key : String(key);
    let descriptor;
    let owner = o;
    for (; owner != null; owner = Object.getPrototypeOf(owner)) {
        descriptor = Object.getOwnPropertyDescriptor(owner, k);
        if (descriptor != null) { break; }
    }

    return owner == null || descriptor == null ? {} : { owner, descriptor };
};
lndexlndex

関数にログ出力を後付で追加する。

ロガーの設定
const withLogger = (f, logger) => {
    const log = logger ?? (...args) => console.debug(...args);
    return new Proxy(f, {
        construct: (target, args, newTarget) => {
            log(f.name, 'new', { target, args, newTarget });
            const result = new target(...args);
            log(f.name, 'new', { result });

            const methods = new WeakMap();

            return new Proxy(result, {
                get: (target, key, receiver) => {
                    log(f.name, 'get', key, { target, receiver });
                    // https://zenn.dev/link/comments/cb9f30fd9fa106
                    const { descriptor } = getPropertyDescriptor(target, key);
                    const get = descriptor?.get;
                    const value = get ?? descriptor?.value;
                    if (typeof value !== 'function') {
                        log(f.name, 'get', key, { value });
                        return value;
                    } else {
                        let method = methods.get(value);
                        if (method == null) {
                            method = withLogger(value, log);
                            methods.set(value, method);
                        }
                        return (get != null) ? method.apply(target) : method; 
                    }
                }
            });
        },
        apply: (target, thisArg, args) => {
            log(target.name, 'apply', { thisArg, args });
            const result = target.apply(thisArg, args);
            log(target.name, 'apply', { thisArg, result });
            return result;
        }
    });
};
lndexlndex

Promise対応関数化する関数

Promise対応関数化する関数
const asPromiseAware = (f) => {  
    const f_ = {  
        [f.name]: function(...args) {
            return args.some(x => (typeof x?.then === 'function')) ?
                Promise.all(args).then(xs => f_.apply(this, xs)) :
                f.apply(this, args)
            ;  
        }      
    }[f.name];  
    return f_;  
};
作成者以外のコメントは許可されていません