同步遍历器
function idMaker() {let index = 0;return {next: function() {return { value: index++, done: false };}};}const it = idMaker();it.next().value // 0it.next().value // 1it.next().value // 2// ...var obj = {[Symbol.iterator]: function () {return {index: 0,next: function () {return { value: index++, done: false}}}}}var it = obj[Symbol.iterator]();it.next();
这里隐含着一个规定,it.next()方法必须是同步的,只要调用就必须立刻返回值。
目前的解决方法是,将异步操作包装成 Thunk 函数或者 Promise 对象,即next()方法返回值的value属性是一个 Thunk 函数或者 Promise 对象,等待以后返回真正的值,而done属性则还是同步产生的。
function idMaker() {let index = 0;return {next: function() {return {value: new Promise(resolve => setTimeout(() => resolve(index++), 1000)),done: false};}};}const it = idMaker();it.next().value.then(o => console.log(o)) // 1it.next().value.then(o => console.log(o)) // 2it.next().value.then(o => console.log(o)) // 3
异步遍历器
异步遍历器的最大的语法特点,就是调用遍历器的next方法,返回的是一个 Promise 对象。
asyncIterator.next().then(({ value, done }) => /* ... */);
一个对象的同步遍历器的接口,部署在Symbol.iterator属性上面
对象的异步遍历器接口,部署在Symbol.asyncIterator属性上面
const asyncIterable = createAsyncIterable(['a', 'b']);const asyncIterator = asyncIterable[Symbol.asyncIterator]();asyncIterator.next().then(iterResult1 => {console.log(iterResult1); // { value: 'a', done: false }return asyncIterator.next();}).then(iterResult2 => {console.log(iterResult2); // { value: 'b', done: false }return asyncIterator.next();}).then(iterResult3 => {console.log(iterResult3); // { value: undefined, done: true }});
异步遍历器的next方法是可以连续调用的,不必等到上一步产生的 Promise 对象resolve以后再调用。这种情况下,next方法会累积起来,自动按照每一步的顺序运行下去。
async function runner() {const writer = openFile('someFile.txt');writer.next('hello');writer.next('world');await writer.return();}runner();
也可以把所有的next方法放在Promise.all方法里面。
const asyncIterable = createAsyncIterable(['a', 'b']);const asyncIterator = asyncIterable[Symbol.asyncIterator]();const [{value: v1}, {value: v2}] = await Promise.all([asyncIterator.next(), asyncIterator.next()]);console.log(v1, v2); // a b
for await …of
for await...of循环,则是用于遍历异步的 Iterator 接口。
async function f() {for await (const x of createAsyncIterable(['a', 'b'])) {console.log(x);}}// a// b
如果next方法返回的 Promise 对象被reject,for await...of就会报错,要用try...catch捕捉。
async function () {try {for await (const x of createRejectingIterable()) {console.log(x);}} catch (e) {console.error(e);}}
注意,for await...of循环也可以用于同步遍历器。
(async function () {for await (const x of ['a', 'b']) {console.log(x);}})();// a// b
