背景
在 ES6 中,Symbol.iterator 成为了 JavaScript 中的一个新特性。通过实现 Symbol.iterator 方法,我们可以自定义对象的迭代器(iterator),使其能够被 for...of 循环遍历。
在 ES9 中,为了增强 Symbol.iterator 特性的能力,允许对一个对象直接使用 for await...of 语法进行异步遍历。这个特性给 JavaScript 开发带来了更多便利,但也带来了一个隐藏的问题。
问题
在 ES9 中,对象的 Symbol.iterator 方法需要返回一个异步的迭代器对象,该对象必须含有一个 next 方法,且该方法返回一个 Promise 对象。但是,我们发现如果在返回 Promise 对象前,迭代器已经被遍历结束,那么 next 方法就会抛出 StopIteration 错误。
这是一个常见的错误,如果不加以解决,就会导致代码出现难以排查的 bug。
解决方案
为了解决这个问题,我们需要在 next 方法中正确地捕获错误。如果捕获到了 StopIteration 错误,就不再返回 Promise 对象,而是直接返回一个 resolved 的 Promise。
以下是解决方案的示例代码:
-- -------------------- ---- ------- ----- ------------- - ------------- - --------- - ---------------------- --------------------- ---------------------- - ------------------------ - --- - - -- --- ---- - ---------- ------ - ------ - --- ------- - ---------- -- ---------- - ------ ----------------- ----- ---- --- - ------ ------------------ -- - ------ - ------ ----- ----- -- -------------- -- - -- ------ ---------- -------------- - ------ ----------------- ----- ---- --- - ---- - ----- ------ - --- - -- - - ------ ---------- - --- -------- - --- ---------------- --- ----- ---- ---- -- --------- - ------------------ - -----
上面的代码中,我们首先创建了一个 AsyncIterable 类,该类实现了 Symbol.asyncIterator 方法,返回的迭代器能够返回一个异步的 Promise 对象。
在迭代器的 next 方法中,我们首先检查是否已遍历完所有元素,如果是,就返回一个 resolved 的 Promise 对象,done 属性为 true。
否则,就取出 data 数组中的下一个 Promise 对象,返回一个新的 Promise 对象。如果 Promise 对象成功 resolve,就将 resolve 的结果作为 value 属性,done 属性为 false,返回包含这两个属性的对象。
如果 Promise 对象失败 reject,我们就检查该错误是否为 StopIteration 错误,如果是,就返回一个 resolved 的 Promise 对象,done 属性为 true。如果不是,就直接抛出错误。
最后,我们使用 for await...of 循环遍历 AsyncIterable 类,这里和 for...of 循环不同的是,在 for await...of 循环中,我们需要加上 await 关键字,保证异步遍历数据。
总结
在本文中,我们讨论了 ES9 中 Symbol.iterator 内部实现的错误问题,并给出了解决方案的示例代码。虽然这个问题比较隐蔽,但只要了解了解决方案,就能够避免它带来的 bug,提高代码的健壮性和清晰度。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/64e443e4f6b2d6eab3fa1aa5