前言
Promise是ES6提出来的一个对象,而且在我们开发中用的非常多。这篇文章主要介绍Promise的一一些高级用法
串行
在提到串行之前,我们首先看一下同步/异步 串行/并行的概念。
同步/异步 串行/并行
同步异步指的是能否开启新的线程。同步不能开启新的线程,异步可以开启。异步分为串行和并行
串行并行指的是任务的执行方式。串行是指多个任务时,各个任务按顺序执行,完成一个之后才能进行下一个。并行指的是多个任务可以同时执行。异步是多个任务并行的前提条件。
实现方式
场景
现有30个异步请求需要发送,但由于某些原因,我们必须将同一时刻并发请求数量控制在5个以内,同时还要尽可能快速的拿到响应结果。
请求的串行:按照规定的次序依次完成请求,一般用于带有依赖关系的请求。
请求的并行:同时执行多个异步请求,对请求先后完成的顺序没有要求。
串行的实现
递归执行:简单来说就是在then方法里面递归传递下一次异步方法:then(next());
循环调用:这种办法比较取巧,直接利用Promise.resolve()。通过循环赋值,得到最终的结果。
- 使用async和await
let fn = async function(promiseArr) {
for(let i = 0, len = arr.length; i < len; i++) {
currentPromise = (promiseArr[i] instanceOf Promise) ? promiseArr[i] : Promise.resolve(promiseArr[i]);
let result = await currentPromise;
console.log(result);
}
}
fn(arr);
- 实现一个请求类,属性包括请求名,属性包括请求名,和请求所需要的时间,以及发送请求的方法(使用promise)
// 方案1
// 请求类
class Request{
// 连接属性
constructor(name, delay) {
this.name = name;
this.delay = delay;
}
// 发送请求的方法
sendRequest() {
let time = this.delay;
return new Promise(function (resolve, reject) {
setTimeout(() => resolve('status: ok' + ' | runtime:' + time + 'ms'), time)
})
}
}
// new两个请求实例
let test1 = new Request('userinfo', 1000);
let test2 = new Request('productinfo', 2000);
// 请求的串行
test1.sendRequest().then(function (result) {
console.log(result);
test2.sendRequest().then(function(result) {
console.log(result);
})
})
// 请求的串行(then中使用箭头函数简化代码)
test1.sendRequest().then((result) => {
console.log(result);
test2.sendRequest().then(result => console.log(result))
})
// 方案2
let createPromise = function(time) {
return (resolve, reject) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('timein' + time);
resolve();
}, time*1000)
})
}
}
function serpromise(arr) {
arr.reduce((pre, next, index, carr) => {
return pre.then(next);
}, Promise.resolve())
}
let arr=[createPromise(2), createPromise(1), createPromise(3), createPromise(4), createPromise(5)];
serpromise(arr);
并行请求的实现
let test1 = new Request('userinfo', 1000);
let test2 = new Request('productinfo', 2000);
let test3 = new Request('productinfo', 5000);
let container = [test1.sendRequest(),test2.sendRequest(),test3.sendRequest()];
Promise.all(container).then(
result => {
console.log(result);
}
)
并行请求的数量限制
假如请求容器中一次产生很多请求需要处理,如果同时发送的话可能造成内存溢出,所以需要对请求进行分段处理。
- 需要一个函数处理并发请求,这个函数要处理一个装着许多,非依赖请求的数组,以及可以同时处理多少个请求数量的参数。
- 因为每次批量处理请求的数量有限制,所以我们还需要一个方法将传入的数组成长度较小的数组
- 因为每个请求都返回一个promise,所以我们用promise.all对较小数组中的请求进行一次包装,使用promise.all对请求进行处理可以使所有请求都达到resolve状态后再执行回调。
function senAll(urls=[], maxNum) {
// 如果全部请求数小于允许的最大值,直接发送全部请求
if (urls.length < maxNum) {
return sendRequest(urls);
} else {
// 对请求容器进行定长剪裁处理,并进行请求完成数量的检测
return 0;
}
// 请求的批量处理
function sendRequest(requests = []) {
// 如果不在数组中调用请求则在调用实例的请求方法
return Promise.all(requests).then(
result => {
console.log(result);
}
)
}
}
使用Promise实现串行(另外方式)
// 一个promise的function
function delay(time) {
return new Promise((resolve, reject) => {
console.log(`wait ${time}s`);
setTimeout(() => {
console.log('execute');
resolve();
}, time * 1000)
})
}
const arr = [3, 4, 5];
- reduce
arr.reduce((s, v) => {
return s.then(() => delay(v));
}, promise.resolve())
- async + 循环 + await
(
async function() {
for (const v of arr) {
await delay(v);
}
}
)
- 普通循环
let p = Promise.resolve();
for(const i of arr) {
p = p.then(() => delay(i))
}
- 递归
function dispatch(i, p = Promise.resolve()) {
if (!arr[i]) return Promise.resolve()
return p.then(() => dispatch(i + 1, delay(arr[i])))
}
dispatch(0)