promise手写题[更新中]

98 阅读3分钟

1. 手写promise

const PENDING = 'pending';
const FULFILLED = 'resolved';
const REJECTED = 'rejected';
function isFunction(fn) {
    return typeof fn === 'function';
}
class MyPromise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;
        this.reason = undefined;

        this.resolvedCallbacks = []
        this.rejectedCallbacks = [];

        const resolve = (value) => {
            if(this.status === PENDING) {
                this.status = FULFILLED;
                this.value = value;
                this.resolvedCallbacks.forEach((callback) => {
                    callback(this.value)
                })
            }
        }

        const reject = (reason) => {
            if(this.status === PENDING) {
                this.status = REJECTED;
                this.reason = reason;
                this.rejectedCallbacks.forEach((callback) => {
                    callback(this.value)
                })
            }
        }

        try {
            executor(resolve, reject)
        } catch (error) {
            reject(error)
        }
    }

    then(onFulfilled, onRejected) {
        onFulfilled = isFunction(onFulfilled) ? onFulfilled : (x) => x;
        onRejected = isFunction(onRejected) ? onRejected : (s) => {throw s };
        const promise2 = new MyPromise((resolve, reject) => {
            const fulfilledMircocallBack = () => {
                queueMicrotask(() => {
                    try {
                        const x = onFulfilled(this.value);
                        resovlePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error)
                    }
                })
            }
            const rejectedMircocallBack = () => {
                queueMicrotask(() => {
                    try {
                        const s = onRejected(this.value);
                        resovlePromise(promise2, s, resolve, reject);
                    } catch (error) {
                        reject(error)
                    }
                })
            }
            if(this.status === FULFILLED) {
                fulfilledMircocallBack();
            }else if(this.status === REJECTED) {
                rejectedMircocallBack();
            } else {
                this.rejectedCallbacks.push(rejectedMircocallBack);
                this.resolvedCallbacks.push(rejectedMircocallBack);
            }
        })

        return promise2;
    }

}
function resovlePromise(promise2, x, resolve, reject) {
    if(promise2 === x) {
        throw new Error('循环引用');
    }

    let done = false;
    if(isFunction(x) || (x !== null && typeof x === 'object')) {
       
        const then = x.then;
        
        if(isFunction(then)) {
            then.call(x, y => {
                resovlePromise(promise2, y, resolve, reject)
            },(r) => {
                if (done) {
                  return;
                }
                done = true;
                reject(r);
              })
        } else {
            if(done) return;
            done = true;
            resolve(x);
        }
    } else {
        if(done) return;
        done = true;
        resolve(x);
    }
   
    
}

2. 任务调度

JS实现一个带并发限制的异步调度器Scheduler,保证同时运行的任务最多有两个。完善代码中Scheduler类,使得以下程序能正确输出

class Scheduler {}


const timeout = (time) => new Promise(resolve => {
    setTimeout(resolve, time)
})

const scheduler = new Scheduler()
const addTask = (time, order) => {
    scheduler
        .add(() => timeout(time))
        .then(() => console.log(order))
}

addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')

这道题的关键点在于:

  1. 如何控制请求的数量
  2. 如何在add后可以继续调用then(一般看到then,就可以考虑promise)
class Scheduler {
    constructor() {
        // 最大任务执行数量
        this.maxTask = 2;
        
        // 继续需要执行的任务
        this.tasks = [];
        
        // 正在执行的任务数量
        this.runningQueue = 0;
    }
    add(task) {
        const promise = new Promise((resolve, reject) => {
            this.tasks.push(() => task.then(resolve, reject));
        });
        this.run();
        return promise;
    }
    run() {
        // 控制请求的数量
        while (this.runningQueue < this.maxQueue && this.tasks.length) {
            _run();
        }
        const _run = () => {
            if (!this.tasks.length) {
                return;
            }
            const task = this.tasks.shift();
            this.runningQueue++;

            task.then(() => {
                this.runningQueue--;
                _run();

            })
        }
    }
    
}

3. 实现一个如下调用

class Task {
}

function task1(next) {
    setTimeout(() => {
        console.log('red')
        next()
    }, 3000)
}

function task2(next, b) {
    setTimeout(() => {
        console.log(b)
        next()
    }, 1000)
}

function task3(next, c) {
    setTimeout(() => {
        console.log('yellow')
        next()
    }, 2000)
}

let task = new Task()
task.add(task1).add(task2, null, 3).add(task3)
task.run()
setTimeout(() => {
    task.stop()
}, 3500)

题目解析:

  • task2的参数是next和b, 难点在在于next参数的内容是什么
class Task {
    constructor() {
        this.tasks = [];
        
        this.isRuning = false;
    }
    add(fn, context, arg) {
        this.tasks.push({ fn, context, arg });
    }
    run() {
        this.isRuning = true;
        const _run = () => {
            if(!this.tasks.length || !this.isRuning) {
                return;
            }
            const { fn, context, arg } = this.tasks.shift();
            fn.call(constext, () => {
                _run();
            }, ...arg);
        }
        _run();
    }
    stop() {
        this.isRuning = false;
    }
}

4. 控制并发数量

顺序加载10张图片,图片地址已知,但是同时最多加载3张图片,要求用promise实现。

const imags = ['images1.png', 'images1.png', 'images1.png', 'images1.png', 'images1.png','images1.png','images1.png','images1.png']
function loadImages(imags) {
    let isLoad = 0;
    let index = 0
    while(isLoad < 3 && index < images.length) {
        cosnt loadImg = () => {
            index++;
            isLoad++;
            return new Promise(() => {
                const url = images[index];
                try {
                    // 加载一张图片
                    let image = new Image();
                    image.onload = function () { resolve(i) }
                    image.onerror = function () { reject(i) };
                    image.src = url;
                } catch (e) {
                    reject(i);
                }
            ).finally(() => {
                isLoad--;
                loadImg();
            })
        }
    }
}

5. 控制并发,并输出全部promise结果

输入: reqs = [req1, req1, ...reqsn]

输出: 成功或者失败都要输出 [1,error,3,4,5,6]

function (reqs, limit = 5){}

function getPromisesRes(req1, limit) {
    let index = 0;
    let isRuning = 0;
    let resNum = 0;
    const res = [];
    return new Promise((resolve, reject) => {
        const getRes = (curIndex) => req1[curIndex]().then((res) => {
            res[curIndex] = res;
        }, err => {
            res[curIndex] = err;
        }).finnaly(() => {
            resNum++;
            isRuning--;
            if(resNum === req1.length) {
                resolve(res);
            }
            getRes(++index);
        });
        while(isRuning < limit && index < req1.lenght) {
            index++;
            getRes(index);
        }
    });
}

6. 使用Promise实现每隔1秒输出1,2,3

function consoleTimeout() {
    cons task = () => new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve();
        }, 1000)
    });
    task.then(() => {
        task();
    })
}

7. 使用Promise实现红绿灯交替重复亮

红灯3秒亮一次,黄灯2秒亮一次,绿灯1秒亮一次;如何让三个灯不断交替重复亮灯?(用Promise实现)三个亮灯函数已经存在:

function task (timeout) {
    reeturn () => new Promise((resolve, reject) => {
        console.log(‘任务开始’)
        setTimeout(() => {
            console.log(‘任务结束’)
        }, timeout);
    })
};
const tasks = [task(3000), task(2000), task(1000)];
let curTaksIndex = 0;
const runTask  = () => {
    const task = tasks[curTaksIndex];
    task().then(res => {
        curTaksIndex++;
        if(curTaksIndex > tasks.length) {
            curTaksIndex = 0;
        }
        runTask();
    })
}
runTask();

8. lazy链式调用

lazy函数可以链式调用,在调用函数的时候不会输出任何内容,当调用output时,输出前面每个函数的执行结果。

const lazyFun = lazy(2).add(2).top(console.log).delay(1000).multipy(3); 
// 此时不会输出任何东西

setTimeout(() => {
    lazyFun.output();
}, 1000);
console.log('start');


// 输出内容
// 'start'
// 等待1000ms
// 4
// 4
// 等待1000ms
// 12

答案:

class Lazy {
    constructor() {
        this.tasks = [];
        this.value = 0;
    }
    lazy(num) {
        this.value = num;
        console.log('lazy', this.value)
        return this;
    }
    add(num) {
        this.tasks.push(() => {
            this.value += num;
            console.log('add', this.value)
        })
        return this;
    }
    top(fn) {
        this.tasks.push(() => {
            fn(this.value);
             console.log('top', this.value)
        });
       
        return this;
    }
    delay(timeout) {
        this.tasks.push({
            type: 'dealy',
            fn: () => new Promise((resolve) => {
               console.log('delay', this.value)
               setTimeout(resolve, timeout) 
            }) 
        });
        return this;
    }
    multipy(num) {
        this.tasks.push(() => {
            this.value *= num;
            console.log('multipy', this.value)
        })
        
        return this;
    }
    async output() {
        for(let item of this.tasks) {
            const isObj = typeof item === 'object';
            // console.log('for item====', item, isObj)
            if(isObj) {
                await item.fn.call(this)
            } else {
                item();
            }
        }
        
    }
    
}