递归
function factorial(n) {
if (n === 1) return 1;
return n * factorial(n - 1);
}
factorial(5) // 120
上面代码是一个阶乘函数,计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n) 。
尾递归
function factorial(n, total) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}
factorial(5, 1) // 120
factorial(4, 5*1)
factorial(3, 4*5*1)
factorial(2, 3*4*5*1)
factorial(1, 2*3*4*5*1) -> return 2*3*4*5*1
改写成尾递归,只保留一个调用记录,复杂度 O(1) 。
数组求和
function sumArray(arr, total = 0) {
if(arr.length === 0) {
return total
}
return sumArray(arr, total + arr.pop())
}
使用尾递归优化求斐波那契数列
function factorial2 (n, start = 1, total = 1) {
if(n <= 2){
return total
}
return factorial2 (n -1, total, total + start)
}
数组扁平化
let a = [1,2,3, [1,2,3, [1,2,3]]]
// 变成
let a = [1,2,3,1,2,3,1,2,3]
// 具体实现
function flat(arr = [], result = []) {
arr.forEach(v => {
if(Array.isArray(v)) {
result = result.concat(flat(v, []))
}else {
result.push(v)
}
})
return result
}
使用js生成1-10000的数组
除了使用循环(for,while,forEach等)外,最简单的是使用Array.from
// 方法一
Array.from(new Array(10001).keys()).slice(1)
// 方法二
Array.from({length: 1000}, (node, i) => i+1)
手写一个Promise
const PENDING = 'pending';
const FULFULLED = 'fulfilled';
const REJECTED = 'rejected';
class MPromise {
constructor(fn) {
this.status = PENDING;
this.value = '';
this.reason = '';
this.resolveMicroQueueTaskList = [];
this.rejectMicroQueueTaskList = [];
fn(this.resolve.bind(this), this.reject.bind(this));
}
resolve(value) {
if (this.status === PENDING) {
this.value = value;
this.status = FULFULLED;
}
}
reject(reason) {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
}
}
get status() {
return this._status;
}
set status(newStatus) {
this._status = newStatus;
if (newStatus === FULFULLED) {
this.resolveMicroQueueTaskList.forEach(cb => {
cb()
});
} else if (newStatus === REJECTED) {
this.rejectMicroQueueTaskList.forEach(cb => {
cb()
});
}
}
then(resolve, reject) {
const resolveFunction = resolve ? resolve : (value) => value;
const rejectFunction = reject ? reject : (reason) => reason;
const nextPromse = new MPromise((resolve, reject) => {
const resolveMicroQueueTask = () => {
queueMicrotask(() => {
const x = resolveFunction(this.value);
this.resolveNextPromise(x, resolve);
})
}
const rejectMicroQueueTask = () => {
queueMicrotask(() => {
const y = rejectFunction(this.reason)
this.resolveNextPromise(y, resolve);
})
}
switch (this.status) {
case PENDING: {
this.resolveMicroQueueTaskList.push(resolveMicroQueueTask);
this.rejectMicroQueueTaskList.push(rejectMicroQueueTask);
break;
}
case FULFULLED: {
resolveMicroQueueTask();
break;
}
case REJECTED: {
rejectMicroQueueTask();
}
}
})
return nextPromse;
}
catch(reject) {
this.then(null, reject);
}
resolveNextPromise (x, resolve) {
resolve(x);
}
static resolve (value) {
if(value instanceof MPromise) {
return value;
}
return new MPromise((resolve, reject) => {
resolve(value);
})
}
static reject(value) {
if(value instanceof MPromise) {
return value;
} else {
return new MPromise((resolve, reject) => {
reject(value);
})
}
}
static race (promiseList) {
let promiseListLen = promiseList.length;
return new MPromise((resolve, reject) => {
if(promiseListLen === 0) {
resolve()
}
for(var i = 0; i< promiseList.length; i++){
MPromise.resolve(promiseList[i]).then(res=> {
resolve(res)
}).catch(err => {
reject(err)
})
}
})
}
static all (promiseList) {
let promiseListLen = promiseList.length;
let j = 0;
let promiseValList = [];
return new MPromise((resolve, reject) => {
if(promiseListLen === 0) {
resolve()
}
for(var i = 0; i< promiseList.length; i++){
MPromise.resolve(promiseList[i]).then(res=> {
j++
promiseValList.push(res);
if(promiseListLen === j) {
resolve(promiseValList)
}
}).catch(err => {
reject(err)
})
}
})
}
}
实现mergePromise函数
实现mergePromise函数,把传进去的数组按顺序先后执行,并且把返回的数据先后放到数组data中。
const time = (timer) => {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, timer)
})
}
const ajax1 = () => time(2000).then(() => {
console.log(1);
return 1
})
const ajax2 = () => time(1000).then(() => {
console.log(2);
return 2
})
const ajax3 = () => time(1000).then(() => {
console.log(3);
return 3
})
function mergePromise () {
// 在这里写代码
}
mergePromise([ajax1, ajax2, ajax3]).then(data => {
console.log("done");
console.log(data); // data 为 [1, 2, 3]
});
// 要求分别输出
// 1
// 2
// 3
// done
// [1, 2, 3]
这道题有点类似于Promise.all(),不过.all()不需要管执行顺序,只需要并发执行就行了。但是这里需要等上一个执行完毕之后才能执行下一个。 解题思路:
- 定义一个数组data用于保存所有异步操作的结果
- 初始化一个
const promise = Promise.resolve()
,然后循环遍历数组,在promise后面添加执行ajax任务,同时要将添加的结果重新赋值到promise上。
function mergePromise (ajaxArray) {
// 存放每个ajax的结果
const data = [];
let promise = Promise.resolve();
ajaxArray.forEach(ajax => {
// 第一次的then为了用来调用ajax
// 第二次的then是为了获取ajax的结果
promise = promise.then(ajax).then(res => {
data.push(res);
return data; // 把每次的结果返回
})
})
// 最后得到的promise它的值就是data
return promise;
}
使用Promise封装一个异步加载图片的方法
这个比较简单,只需要在图片的onload函数中,使用resolve返回一下就可以了。
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function() {
resolve(img);
};
img.onerror = function() {
reject(new Error('Could not load image at' + url));
};
img.src = url;
});
}
使用Promise实现:限制异步操作的并发个数,并尽可能快的完成全部
有8个图片资源的url,已经存储在数组urls中。
urls类似于['https://image1.png', 'https://image2.png', ....]
而且已经有一个函数function loadImg
,输入一个url链接,返回一个Promise,该Promise在图片下载完成的时候resolve,下载失败则reject。
但有一个要求,任何时刻同时下载的链接数量不可以超过3个。
请写一段代码实现这个需求,要求尽可能快速地将所有图片下载完成。
var urls = [
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting1.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting2.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting3.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting4.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting5.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn6.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn7.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn8.png",
];
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function() {
console.log("一张图片加载完成");
resolve(img);
};
img.onerror = function() {
reject(new Error('Could not load image at' + url));
};
img.src = url;
});
}
// 答案
function limitLoad(urls, handler, limit) {
let sequence = [].concat(urls); // 复制urls
// 这一步是为了初始化 promises 这个"容器"
let promises = sequence.splice(0, limit).map((url, index) => {
return handler(url).then(() => {
// 返回下标是为了知道数组中是哪一项最先完成
return index;
});
});
// 注意这里要将整个变量过程返回,这样得到的就是一个Promise,可以在外面链式调用
return sequence
.reduce((pCollect, url) => {
return pCollect
.then(() => {
return Promise.race(promises); // 返回已经完成的下标
})
.then(fastestIndex => { // 获取到已经完成的下标
// 将"容器"内已经完成的那一项替换
promises[fastestIndex] = handler(url).then(
() => {
return fastestIndex; // 要继续将这个下标返回,以便下一次变量
}
);
})
.catch(err => {
console.error(err);
});
}, Promise.resolve()) // 初始化传入
.then(() => { // 最后三个用.all来调用
return Promise.all(promises);
});
}
limitLoad(urls, loadImg, 3)
.then(res => {
console.log("图片全部加载完毕");
console.log(res);
})
.catch(err => {
console.error(err);
});