高级异步单例模式

389 阅读1分钟

背景原因

因为项目中A数据过于庞大,每次请求时间较长,为了减少对A数据的请求,也为了保证项目中所用到的A数据是同一份数据。

什么是单例模式

单例模式就是保证一个类仅有一个实例,并提供一个访问它的全局访问点。

单例模式实现

class ShopInfoData {
    constructor() {
        this.shopData = [];
    }

    async get() {
        return this.shopData;
    }

}

ShopInfoData.getInstance = (function() {
    let instance;
    return function() {
        if (!instance) {
            instance = new ShopInfoData();
        }
        return instance;
    };
})();

获取常量的数据可以通过上述方式过去,那怎么从接口获取数据呢???

class ShopInfoData {
    constructor() {
        this.shopData = [];
        this.count = 1;
    }

    async get() {
        if (!this.shopData.length) {
            await this.refresh();
        }

        return this.shopData;
    }

    // 获取数据
    async refresh() {
        await new Promise(resolve => {
            setTimeout(() => {
                console.log('进来了多少次')
                this.shopData = [this.count++];
                resolve(this.count)
            }, 1000);
        });
    }
}

ShopInfoData.getInstance = (function() {
    let instance;
    return function() {
        if (!instance) {
            instance = new ShopInfoData();
        }
        return instance;
    };
})();

通过上述代码已经实现异步获取数据的单例,后续使用的数据也是同一份且不会多次请求数据进行更新。

你以为功能完成了,万事大吉了么? 不不不,初始化获取实例时还是会多次调用refresh函数,和直调用一次有冲突,这就是单例模式出现竞争状态导致的

 class ShopInfoData {
    constructor() {
        this.shopData = [];
        this.pendingPromise;
    }

    async get() {
        if (!this.shopData.length) {
            await this.refresh();
        } else {
            return this.shopData;
        }

        return this.pendingPromise.then(res => {
            return this.shopData;
        });
    }
    
    // 获取数据
    async refresh() {
        if (!this.pendingPromise) {
            this.pendingPromise = new Promise(resolve => {
                setTimeout(() => {
                    console.log('进来了多少次')
                    this.shopData = [1];
                    resolve(1);
                }, 1000);
            });
        }

        return this.pendingPromise;
    }
    
    // 需要重新获取数据的时候重置pendingPromise
    async refreshAndGet(sourceFilters, statusFilters, showNoShop = false) {
        // 重置pendingPromise,会重新请求接口
        this.pendingPromise = undefined;

        return this.get();
    }
}

ShopInfoData.getInstance = (function() {
    let instance;

    return function() {
        if (!instance) {
            instance = new ShopInfoData();
        }
        return instance;
    };
})();

到此,就结束了!!!