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')
这道题的关键点在于:
- 如何控制请求的数量
- 如何在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();
}
}
}
}