JavaScript设计模式(三十三):入场仪式-等待者模式

简介: 入场仪式-等待者模式

等待者模式(waiter)

通过对多个异步进程监听,来触发未来发生的动作。(类似于 Promise Promise.all(...)

什么是等待者模式

等待者模式或者说等待者对象用来解决那些不确定先后完成的异步逻辑的

比如:运动会的入场仪式,你不确定请哪只队伍先入场,但有一点你很确定,就是会议开始必须等到所有的队伍入场完毕。而这里的会议开始就相当于等待这模式的逻辑执行

Promise.all(...)

function fnA() {
   
    let code = [0, 1][Math.floor(Math.random() * 2)];
    return new Promise((resolve, reject) => {
   
        setTimeout(function () {
   
            if (code) resolve({
    code, msg: 'success!', name: 'fnA' })
            else reject({
    code, msg: 'fail!', name: 'fnA' })
        }, 1500);
    });
}

function fnB() {
   
    let code = 1;
    return new Promise((resolve, reject) => {
   
        setTimeout(function () {
   
            if (code) resolve({
    code, msg: 'success!', name: 'fnB' })
            else reject({
    code, msg: 'fail!', name: 'fnB' })
        }, 1000);
    });
}

Promise.all([fnA(), fnB()])
    .then(res => {
   
        /**
         * [{ code: 1, msg: 'success!', name: 'fnA' }, 
         *  { code: 1, msg: 'success!', name: 'fnB' }]
         */
        console.log(res);
    })
    .catch(err => {
   
        // { code: 0, msg: 'fail!', name: 'fnA' }
        console.log(err);
    });

函数形参嵌套函数形参

/**
 * 函数形参fn 嵌套 函数形参resolve、reject
 * @param {Function} fn demo的函数行参
 *      @param {Function} resolve fn的函数形参1
 *      @param {Function} reject  fn的函数形参2
 */
function demo(fn = (resolve, reject) => {
    resolve(null); reject(null); }) {
   
    fn(resolve, reject);
    function resolve(...args) {
    console.log('resolve', args); }
    function reject(...args) {
    console.log('reject', args); }
}

demo(); // resolve->[null] reject->[null]

demo((resolve, reject) => {
   
    resolve(1, 2, 3);
    reject(4, 5, 6);
}); // resolve->[1, 2, 3] reject->[4, 5, 6]

以上代码的变式1

等待者模式完成 40%

/**
 * 函数形参fn 嵌套 函数形参resolve、reject
 * @param {Function} fn Demo的函数行参
 *      @param {Function} resolve fn的函数形参1
 *      @param {Function} reject fn的函数形参2
 */
function Demo(fn = (resolve, reject) => {
    resolve(null); reject(null); }) {
   
    let value = [];
    fn(resolve, reject);
    function resolve(...args) {
   
        value = [...value, ...args];
    }
    function reject(...args) {
   
        value = [...value, ...args];
    }
    this.then = (callback) => {
   
        callback(value);
    }
}

new Demo((resolve, reject) => {
   
    resolve(1, 2, 3);
    reject(4, 5, 6);
}).then(res => console.log(res)); // (6) [1, 2, 3, 4, 5, 6]

new Demo().then(res => console.log(res)); // [null, null]

以上代码的变式2

等待者模式完成 80%

/**
 * 函数形参fn 嵌套 函数形参resolve、reject
 * @param {Function} fn Demo的函数行参
 *      @param {Function} resolve fn的函数形参1
 *      @param {Function} reject fn的函数形参2
 */
function Demo(fn) {
   

    let state = null; // null->赋值操作  true->resolve、onFulfilled  false->reject、onRejected
    let value = [];
    let deferred = {
   };

    fn(resolve, reject);

    function resolve(...args) {
   
        state = true;
        value = args;
        deferred.onFulfilled && deferred.onFulfilled(value);
    }

    function reject(...args) {
   
        state = false;
        value = args;
        deferred.onRejected && deferred.onRejected(value);
    }

    this.then = (onFulfilled, onRejected) => {
   
        if (state === null) {
   
            deferred = {
    onFulfilled, onRejected }
        } else {
   
            (state ? onFulfilled : onRejected)(value);
        }
        return this;
    }
}

new Demo((resolve, reject) => {
   
    setTimeout(function () {
   
        resolve(1, 2, 3);
        reject(4, 5, 6);
    }, 2000);
}).then(res => {
   
    console.log('res', res); // res->[1, 2, 3]
}, err => {
   
    console.log('err', err); // err->[4, 5, 6]
});

自定义实现等待者模式(仿Promise.all)

等待者模式完成 100%

/**
 * 等待者模式
 * @param {Function} fn (resolve, reject) => { resolve('ok'); reject('fail'); }
 * @returns 实例化
 */
function Waiter(fn) {
   

    // 兼容不使用new关键字
    if (!(this instanceof Waiter)) return new Waiter(fn);

    let state = null;   // true 成功回调; false 失败回调; null 中立
    let value = null;   // 暂存`成功或失败回调的结果`,用于`后续调用.then`的`使用`
    let deferred = {
   };  // 存储.then(...)传入的参数 ---> { onFulfilled, onRejected }

    fn(resolve, reject);

    /**
     * 接收成功失败回调
     * @param {Function} onFulfilled 成功回调
     * @param {Function} onRejected  失败回调
     * @returns 当前实例
     */
    this.then = function (onFulfilled, onRejected) {
   
        if (state === null) {
   
            deferred = {
    onFulfilled, onRejected };
        } else {
   
            (state ? onFulfilled : onRejected)(value);
        }
        return this;
    };

    /**
     * 成功回调
     * @param {any} val 结果
     */
    function resolve(val) {
   
        if (state !== null) return;
        state = true;
        value = val;
        deferred.onFulfilled && deferred.onFulfilled(value);
        deferred = null;
    }

    /**
     * 失败回调
     * @param {any} val 结果
     */
    function reject(val) {
   
        if (state !== null) return;
        state = false;
        value = val;
        deferred.onRejected && deferred.onRejected(value);
        deferred = null;
    }

}

/**
 * 等待所传函数全部完成
 * @param  {...any} args 任意参数
 * @returns Waiter实例
 */
Waiter.all = (...args) => {
   
    // 如果参数只有一个并且是数组,那么使用这个数组;如果参数为多个,那么使用参数组合成的数组;
    let arr = (args.length === 1 && Array.isArray(args[0])) ? args[0] : args;
    return new Waiter(function (resolve, reject) {
   
        // 如果未传参,则返回空数组
        if (arr.length === 0) return resolve([]);
        // 还需处理的对象数量计数器
        let remaining = arr.length;
        // 处理传参保存结果函数
        function result(w, i) {
   
            if (w instanceof Waiter) {
   
                w.then(function (res) {
    result(res, i); }, reject);
                return;
            }
            // 完成一个替换一个
            arr[i] = w;
            if (--remaining === 0) {
   
                resolve(arr);
            }
        }
        arr.forEach((w, i) => result(w, i));
    });
};
const w1 = new Waiter((resolve, reject) => {
   
    setTimeout(() => {
   
        Math.random() > 0.5 ? resolve(1) : reject(0);
    }, 2000);
}).then(res => console.log('res', res), err => console.log('err', err));
// 返回对象,成功或失败回调
function fnA() {
   
    return new Waiter((resolve, reject) => {
   
        setTimeout(() => {
   
            Math.random() > 0.5 ? resolve({
    name: 'A', msg: 'success' }) : reject({
    name: 'A', msg: 'fail' });
        }, 100);
    });
}
// 返回对象,成功回调
function fnB() {
   
    return new Waiter((resolve, reject) => {
   
        setTimeout(() => {
   
            resolve({
    name: 'B', msg: 'success' });
        }, 3000);
    });
}
// 返回数字,成功回调
function fnC() {
   
    return new Waiter((resolve, reject) => {
   
        setTimeout(() => {
   
            resolve(123);
        }, 1000);
    });
}

const w2 = Waiter.all([fnB(), fnA(), fnC(), 'abc', 456])
    .then(res => console.log(JSON.stringify(res)), err => console.log(err));

等待者模式(ES6写法)

class Waiter {
   
    #state = null;  // true 成功回调; false 失败回调; null 中立
    #value = null;  // 暂存`成功或失败回调的结果`,用于`后续调用.then`的`使用`
    #deferred = {
   }; // 存储.then(...)传入的参数 ---> { onFulfilled, onRejected }
    constructor(fn) {
   
        // 兼容不使用new关键字
        if (!(this instanceof Waiter)) return new Waiter(fn);
        fn(this.#resolve, this.#reject);
    }
    /**
     * 接收成功失败回调
     * @param {Function} onFulfilled 成功回调
     * @param {Function} onRejected  失败回调
     * @returns 当前实例
     */
    then = function (onFulfilled, onRejected) {
   
        if (this.#state === null) {
   
            this.#deferred = {
    onFulfilled, onRejected };
        } else {
   
            (this.#state ? onFulfilled : onRejected)(this.#value);
        }
        return this;
    };
    /**
     * 成功回调
     * @param {any} val 结果
     */
    #resolve = (val) => {
   
        if (this.#state !== null) return;
        this.#state = true;
        this.#value = val;
        this.#deferred.onFulfilled && this.#deferred.onFulfilled(this.#value);
        this.#deferred = null;
    };
    /**
     * 失败回调
     * @param {any} val 结果
     */
    #reject = (val) => {
   
        if (this.#state !== null) return;
        this.#state = false;
        this.#value = val;
        this.#deferred.onRejected && this.#deferred.onRejected(this.#value);
        this.#deferred = null;
    };
    /**
     * 等待所传函数全部完成
     * @param  {...any} args 任意参数
     * @returns Waiter实例
     */
    static all = (...args) => {
   
        // 如果参数只有一个并且是数组,那么使用这个数组;如果参数为多个,那么使用参数组合成的数组;
        let arr = (args.length === 1 && Array.isArray(args[0])) ? args[0] : args;
        return new Waiter(function (resolve, reject) {
   
            // 如果未传参,则返回空数组
            if (arr.length === 0) return resolve([]);
            // 还需处理的对象数量计数器
            let remaining = arr.length;
            // 处理传参保存结果函数
            function result(w, i) {
   
                if (w instanceof Waiter) {
   
                    w.then(function (res) {
    result(res, i); }, reject);
                    return;
                }
                // 完成一个替换一个
                arr[i] = w;
                if (--remaining === 0) {
   
                    resolve(arr);
                }
            }
            arr.forEach((w, i) => result(w, i));
        });
    };
}
const w1 = new Waiter((resolve, reject) => {
   
    setTimeout(() => {
   
        Math.random() > 0.5 ? resolve({
    msg: 'success' }) : reject({
    msg: 'fail' });
    }, 5000);
}).then(res => console.log('res', res), err => console.log('err', err));
// 返回对象,成功或失败回调
function fnA() {
   
    return new Waiter((resolve, reject) => {
   
        setTimeout(() => {
   
            Math.random() > 0.5 ? resolve({
    name: 'A', msg: 'success' }) : reject({
    name: 'A', msg: 'fail' });
        }, 100);
    });
}
// 返回对象,成功回调
function fnB() {
   
    return new Waiter((resolve, reject) => {
   
        setTimeout(() => {
   
            resolve({
    name: 'B', msg: 'success' });
        }, 3000);
    });
}
// 返回数字,成功回调
function fnC() {
   
    return new Waiter((resolve, reject) => {
   
        setTimeout(() => {
   
            resolve(123);
        }, 1000);
    });
}

const w2 = Waiter.all([fnB(), fnA(), fnC(), 'abc'])
    .then(res => console.log(JSON.stringify(res)), err => console.log(err));
目录
相关文章
|
22天前
|
设计模式 SQL 算法
设计模式了解哪些,模版模式
设计模式了解哪些,模版模式
21 0
|
2月前
|
设计模式 Java uml
C++设计模式之 依赖注入模式探索
C++设计模式之 依赖注入模式探索
37 0
|
4月前
|
设计模式 存储 算法
Java 设计模式最佳实践:三、行为模式
Java 设计模式最佳实践:三、行为模式
22 0
|
3月前
|
设计模式 前端开发 JavaScript
观察者模式 vs 发布-订阅模式:两种设计模式的对决!
欢迎来到前端入门之旅!这个专栏是为那些对Web开发感兴趣、刚刚开始学习前端的读者们打造的。无论你是初学者还是有一些基础的开发者,我们都会在这里为你提供一个系统而又亲切的学习平台。我们以问答形式更新,为大家呈现精选的前端知识点和最佳实践。通过深入浅出的解释概念,并提供实际案例和练习,让你逐步建立起一个扎实的基础。无论是HTML、CSS、JavaScript还是最新的前端框架和工具,我们都将为你提供丰富的内容和实用技巧,帮助你更好地理解并运用前端开发中的各种技术。
|
18天前
|
设计模式 Java 数据库
小谈设计模式(2)—简单工厂模式
小谈设计模式(2)—简单工厂模式
|
3天前
|
设计模式 消息中间件 Java
Java 设计模式:探索发布-订阅模式的原理与应用
【4月更文挑战第27天】发布-订阅模式是一种消息传递范式,被广泛用于构建松散耦合的系统。在 Java 中,这种模式允许多个对象监听和响应感兴趣的事件。
20 2
|
6天前
|
设计模式 存储 JavaScript
[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式
[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式
|
6天前
|
设计模式 Java Go
[设计模式Java实现附plantuml源码~创建型] 集中式工厂的实现~简单工厂模式
[设计模式Java实现附plantuml源码~创建型] 集中式工厂的实现~简单工厂模式
|
8天前
|
设计模式
设计模式(一)简单工厂模式
设计模式(一)简单工厂模式
14 0
|
18天前
|
设计模式 Java
小谈设计模式(9)—工厂方法模式
小谈设计模式(9)—工厂方法模式