Promise的应用

50 阅读3分钟

前言

Promise是ES6提出来的一个对象,而且在我们开发中用的非常多。这篇文章主要介绍Promise的一一些高级用法

串行

在提到串行之前,我们首先看一下同步/异步 串行/并行的概念。

同步/异步 串行/并行

同步异步指的是能否开启新的线程。同步不能开启新的线程,异步可以开启。异步分为串行和并行
串行并行指的是任务的执行方式。串行是指多个任务时,各个任务按顺序执行,完成一个之后才能进行下一个。并行指的是多个任务可以同时执行。异步是多个任务并行的前提条件。

实现方式

场景

现有30个异步请求需要发送,但由于某些原因,我们必须将同一时刻并发请求数量控制在5个以内,同时还要尽可能快速的拿到响应结果。
请求的串行:按照规定的次序依次完成请求,一般用于带有依赖关系的请求。
请求的并行:同时执行多个异步请求,对请求先后完成的顺序没有要求。

串行的实现

递归执行:简单来说就是在then方法里面递归传递下一次异步方法:then(next());
循环调用:这种办法比较取巧,直接利用Promise.resolve()。通过循环赋值,得到最终的结果。

  1. 使用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);
  1. 实现一个请求类,属性包括请求名,属性包括请求名,和请求所需要的时间,以及发送请求的方法(使用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);
    }
)

并行请求的数量限制

假如请求容器中一次产生很多请求需要处理,如果同时发送的话可能造成内存溢出,所以需要对请求进行分段处理。

  1. 需要一个函数处理并发请求,这个函数要处理一个装着许多,非依赖请求的数组,以及可以同时处理多少个请求数量的参数。
  2. 因为每次批量处理请求的数量有限制,所以我们还需要一个方法将传入的数组成长度较小的数组
  3. 因为每个请求都返回一个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];
  1. reduce
arr.reduce((s, v) => {
    return s.then(() => delay(v));
}, promise.resolve())
  1. async + 循环 + await
(
    async function() {
        for (const v of arr) {
            await delay(v);
        }
    }
)
  1. 普通循环
let p = Promise.resolve();
for(const i of arr) {
    p = p.then(() => delay(i))
}
  1. 递归
function dispatch(i, p = Promise.resolve()) {
 if (!arr[i]) return Promise.resolve()
 return p.then(() => dispatch(i + 1, delay(arr[i])))
}
dispatch(0)