借助Iterable object(可迭代对象)实现Promise并发控制

196 阅读2分钟

借助Iterable object(可迭代对象)实现Promise并发控制

什么是Iterator object

可迭代(Iterable) 对象是数组的泛化

也就是说、任何对象都可以通过迭代器来使用 for...of语法来循环遍历

举个例子:

我们常见的数组、数组本身就具有循环遍历的能力

//假如我有很多个女朋友
var girlFriends=["alice","lina","rose"];
//我想知道她们的名字
for(var girlFriend of grilFriends){
  console.log(girlFriend)
}
//原来我有三个女朋友
//output:alice lina rose

迭代对象不仅仅限于数组,比如我们自定义一个对象,那么如何在自定义对象上使用迭代器呢?

方法如下:

如何构建一个Iterator object

可迭代对象、简单的来说就是给对象添加一个Symbol.iterator方法、该方法返回一个含有next方法的对象,如下代码展示

var obj={};
//当`for..of`执行的时候、首先会执行该方法,该方法放回的对象、则就是迭代器
obj[Symbol.iterator]=function(){
  return {
    next:function(){
      //next方法返回一个含有done属性的对象、用于标记循环是否结束、还有一个value属性、用于返回当前的值
      return {
        done:true
        value:''
      }
    }
  }
}

for..of执行的时候、会执行Symbol.iterator方法,获取到迭代器。后面的循环都将使用该迭代器就行遍历

var girlFriendAgeRange= {
    min: 18,
    max: 100,
};
​
//我希望每个年龄的都有一个
//for(var age in girlFriendAgeRange)
//输出:18,19,20...100

根据上文说到的、定义一个Symbol.iterator方法

var girlFriendAgeRange = {
    min: 18,
    max: 100,
};
girlFriendAgeRange[Symbol.iterator] = function () {
    return {
        //由于next的执行上下文、是当前对象、所以我们把我们需要的数据、绑定到当前上线文、便于后续使用
        current: this.min,
        max: this.max,
        next: function () {
            if (this.current <= this.max) {
                return { done: false, value: this.current++ };
            } else {
                return { done: true };
            }
        },
    };
};
for (var age of girlFriendAgeRange) {
    console.log(age);
}
//这样我们就有很多个女朋友了

迭代器总的来说、就是这么简单

如何用迭代器对象来控制Promise的并发限制呢

前面说的迭代器都是自动运行的情况、还有一中情况、我们可以手动控制迭代器的访问,比如:

//我们有三个女朋友
var girlFriends=["alice","lina","rose"];
//我们知道数组是含有迭代器对象的、所以我们直接获取到就行
var iterator = girlFriends[Symbol.iterator]();
let first=iterator.next();
console.log(first.value)//输出alice
let second=iterator.next();
console.log(first.value)//输出lina

所以可以通过手动控制迭代器对象的访问的方式来实现一个Promise的并发限制

直接上代码:

//模拟10个异步请求
var data = new Array(10).fill("").map((item, i) => {
    let index = i;
    return function (callback) {
        console.log(index + " promise start");
        setTimeout(() => {
            console.log(index + " promise end");
            callback();
        }, Math.random() * 8000);
    };
});

class PromisePool {
    constructor(promise, queueSize) {
        this.promise = promise;
        this.queueSize = queueSize || 5;
        this.count = 0;
        this.iterator = this.promise[Symbol.iterator]();
    }
    request(fn) {
        fn(() => {
            this.count--;
            this.run();
        });
    }

    run() {
        if (this.count < this.queueSize) {
            let next = this.iterator.next();
            if (!next.done) {
                this.count++;
                this.request(next.value);
                this.run();
            }
        } else {
            console.log("wait a promise done ");
        }
    }
}

new PromisePool(data, 5).run();

​
//输出
0 promise start
1 promise start
2 promise start
3 promise start
4 promise start
wait a promise done 
1 promise end
5 promise start
wait a promise done 
5 promise end
6 promise start
wait a promise done 
3 promise end
7 promise start
wait a promise done 
7 promise end
8 promise start
wait a promise done 
4 promise end
9 promise start
wait a promise done 
2 promise end
8 promise end
9 promise end
0 promise end
6 promise end

总结

  • 可以使用for..of的对象、是可迭代的
  • 可迭代对象必须实现 Symbol.iterator 方法
  • Symbol.iterator 方法返回的对象称为迭代器
  • 迭代器需要含有next方法
  • Symbol.iterator 方法会被 for..of 自动调用,也可以手动调用