这里不讲仿写,主要讲本身的所得;最近又回顾了一下promise
的实现;以及async-await
,promise 串行,限流并行;妙哉妙哉。vue
promise
的关键点在于进度条(状态机)的控制最值得改进本身曾经代码的就是它;不少时候咱们请求完接口,接着又要请求下一个,可能就嵌套着写了,ios
ajax(12).then(res =>
ajax(res).then(res1 =>
ajax(res1).then(res2 =>
ajax(res2).then(val => {
console.log(val);
})
)
)
);
复制代码
远观代码,咦,整齐划一,其实不该该这样写的,曾经的你中招了嘛~~~ 悄悄的去改改吧ajax
ajax(1)
.then(res => ajax(res))
.then(res1 => ajax(res1))
.then(res2 => ajax(res2))
.then(val => {
console.log(val);
})
.catch(err => err); //再来一个兜底
复制代码
近观慢动做,也是整齐划一,妙哉妙哉axios
上面的代码例子就是一个串行请求,能够写一个通用的函数数组
//values: () => Promise<any>[]
function promiseReduce(values) {
return values.reduce(
(p, n) =>
(p = p.then(
() => n(),
err => err
)),
Promise.resolve() //默认值加上
);
}
复制代码
new Promise(res => {
let result = [];
ajax(1)
.then(res => {
result.push(res);
return ajax(2);
})
.then(res1 => {
result.push(res1);
return ajax(3);
})
.then(res2 => {
result.push(res2);
return ajax(4);
})
.then(res3 => {
result.push(res3);
res(result);
})
.catch(err => err);
}).then(data => {
console.log(data);
});
复制代码
let result = [];
ajax(1)
.then(val => Promise.resolve(result.push(val)))
.then(() => ajax(2))
.then(val => Promise.resolve(result.push(val)))
.then(() => ajax(3))
.then(val => Promise.resolve(result.push(val)))
.then(() => ajax(4))
.then(val => Promise.resolve(result.push(val)))
.then(() => {
console.log(result, 'f');
})
.catch(err => err);
复制代码
改进上面代码promise
list = [() => ajax(), () => ajax(), ...]
,那就将其改为[() => ajax(), Promise.resolve(result.push(val))), () => ajax(), Promise.resolve(result.push(val))),...]
promiseReduce
function g(val) {
val && result.push(val);
}
function promiseReduce(values, fn) {
return values.reduce((p, n) => {
return (p = p.then(
val => Promise.resolve((fn(val), val)).then(res => n(res)),
err => err
));
}, Promise.resolve());
}
//也能够这样写
function promiseReduce1(values, fn) {
return values.reduce((p, n) => {
return (p = p.then(
val => Promise.resolve((fn(val), val)),
err => err
)).then(() => n());
}, Promise.resolve());
}
复制代码
对于 reject 的状况,能够每一个 promise 兜底,也能够最后兜底。可是结果不同 调试代码以下bash
function ajax(val) {
return new Promise((res, rej) => {
setTimeout(() => {
res(val);
}, 1000);
});
}
function g(val) {
val && result.push(val);
}
function promiseReduce(values, fn) {
return values.reduce((p, n) => {
return (p = p.then(
val => Promise.resolve((fn(val), val)).then(res => n(res)),
err => err
));
}, Promise.resolve());
}
function reject(val) {
return new Promise((res, rej) => {
setTimeout(() => {
rej('error');
}, 1000);
});
}
//最后兜底
// function promiseReduce(values, fn) {
// return values.reduce((p, n) => {
// return (p = p.then(val => Promise.resolve((fn(val), val)))).then(() => n());
// }, Promise.resolve());
// }
promiseReduce(
[() => reject(), () => ajax(1), () => ajax(2), () => ajax(3), , () => ajax(4), () => ajax(5)],
g
)
.then(val => {
result.push(val);
console.log(result);
})
.catch(err => {
console.log(err);
});
复制代码
promise.all
,也能够改为串行//每隔两秒去请求
function ajax(val) {
return new Promise((resovle, reject) => {
setTimeout(() => {
resovle(val);
console.log(val, 'val');
}, val * 1000);
}).catch(err => err);
}
let l = [ajax, ajax, ajax, ajax, ajax];
let config = 2;
let p = Promise.resolve(config);
function fn(p) {
return function (e, i) {
return (p = p.then(res => e(res))); //这边玩法挺多的😄,有待玩耍
};
}
l = l.map(fn(p));
Promise.all(l).then(res => {
console.log(res);
});
复制代码
promise.all
能够用来实现限流 ,能够看这篇文章,颇有意思我罗列了几个关键点,欢迎食用😋并发
promise
,后面都加入回调队列中build (fn) {
if (this.count < this.limit) {
return this.run(fn)
} else {
return this.enqueue(fn)
}
}
复制代码
async run (fn) {
this.count++
// 维护一个计数器
const value = await fn()
this.count--
// 执行完,看看队列有东西没
this.dequeue()
return value
}
复制代码
下面这段代码能作到,app
其一:利用达到限流后加入的promise
是新的promise
,放入promise.all
中,异步
其二:新的promise
它的状态改变后返回的结果,也就是最后promise.all()
返回到数组中的结果元素
其三: 新的promise
将状态控制权放入回调队列中,等待去执行this.run(fn).then(resolve).catch(reject)
enqueue (fn) {
//新搞一个promise
return new Promise((resolve, reject) => {
this.queue.push({ fn, resolve, reject })
})
}
dequeue () {
if (this.count < this.limit && this.queue.length) {
const { fn, resolve, reject } = this.queue.shift()
//await fn() 执行完将结果带入resolve(value)并结束其状态
this.run(fn).then(resolve).catch(reject)
}
}
复制代码
核心我认为是:promise
的进度条怎么控制或者怎么转移,你们都知道是经过resolve
或者reject
。怎么控制,怎么玩它就是很大的学问了;每一个人的想法思路不一样,各类组合实现就成了万花筒,妙哉妙哉
由于一个promise
的自身的状态是惟一的,由pending
开始到resolved
或者rejected
(这边用这两个名词代替); then
中的返回值是一个接力棒(并始终返回一个新的promise
);又是一个新的开始。进度条控制也就转交给了它。 axios 中间件,async-await 仿写,限流等都有用到
页面上有一个输入框,两个按钮,A 按钮和 B 按钮,点击 A 或者 B 分别会发送一个异步请求,请求完成后,结果会显示在输入框中。
我以前第一想法是 维护一份回调队列,每次点击往队列中加入回调。开关的控制在第一次这个思路相似 vue 的合并更新策略。其实这个思路是错的。理由以下: 主要缘由就是:
js去执行a.click(),b.click()
和用户点击按钮a,按钮b
实际上是不同的,a.click()
,其中有微任务的加到微任务队列中,有宏任务的加入宏任务中;再去执行b.click()
同理;主线程完事了,再去微任务,而后宏任务;a.click()
的全部任务,而后再去执行b.click()
2.正确解法
const p = Promise.resolve();
function AClick() {
p = p.then(() => {
return new Promise((resolve, reject) => {
ajax().then(() => {
resolve();
console.log('A');
});
});
});
}
function BClick() {
p = p.then(() => {
return new Promise((resolve, reject) => {
ajax().then(() => {
resolve();
console.log('B');
});
});
});
}
复制代码
若是按钮不少能够写个函数对每一个按钮包装一层,相似装饰器