nodejs异步迭代器 详解nodejs中的异步迭代器
浅笑· 人气:0前言
从 Node.jsv10.0.0 开始,异步迭代器就出现中了,最近它们在社区中的吸引力越来越大。在本文中,我们将讨论异步迭代器的作用,还将解决它们可能用于什么目的的问题。
什么是异步迭代器
那么什么是异步迭代器?它们实际上是以前可用的迭代器的异步版本。当我们不知道迭代的值和最终状态时,可以使用异步迭代器,最终我们得到可以解决{value:any,done:boolean}对象的 promise。我们还获得了 for-await-of 循环,以帮助我们循环异步迭代器。就像 for-of 循环是针对同步迭代器一样。
const asyncIterable = [1, 2, 3]; asyncIterable[Symbol.asyncIterator] = async function*() { for (let i = 0; i < asyncIterable.length; i++) { yield { value: asyncIterable[i], done: false } } yield { done: true }; }; (async function() { for await (const part of asyncIterable) { console.log(part); } })();
与常规的 for-of 循环相反,for-await-of 循环将会 等待它收到的每个 promise 解析后再继续执行下一个。
除了流,当前没有太多支持异步迭代的结构,但是可以将符号手动添加到任何可迭代的结构中,如此处所示。
作为异步迭代器流
异步迭代器在处理流时非常有用。可读流、可写流、双工流和转换流都支持异步迭代器。
async function printFileToConsole(path) { try { const readStream = fs.createReadStream(path, { encoding: 'utf-8' }); for await (const chunk of readStream) { console.log(chunk); } console.log('EOF'); } catch(error) { console.log(error); } }
如果以这种方式编写代码,则不必通过迭代来获取每个数据块时监听data和end事件,并且 for-await-of 循环随着流本身结束而结束。
调用有分页功能的 API
你还可以用异步迭代从使用分页的源中轻松获取数据。为此,我们还需要一种从 Node https 请求方法提供给我们的流中重构响应主体的方法。也可以在这里使用异步迭代器,因为 https 请求和响应是 Node 中的流:
const https = require('https'); function homebrewFetch(url) { return new Promise(async (resolve, reject) => { const req = https.get(url, async function(res) { if (res.statusCode >= 400) { return reject(new Error(`HTTP Status: ${res.statusCode}`)); } try { let body = ''; /* 代替 res.on 侦听流中的数据, 我们可以用 for-await-of,并附加 data chunk 到响应主体的其余部分 */ for await (const chunk of res) { body += chunk; } // 处理没有 body 的情况 if (!body) resolve({}); // 我们需要解析正文以获取 json,因为它是一个字符串 const result = JSON.parse(body); resolve(result); } catch(error) { reject(error) } }); await req; req.end(); }); }
我们将向Cat API发出请求,以 10 张为一组获取一些猫的图片。我们还将在请求之间添加 7 秒的延迟,最大页面数为5,以避免导致 cat API 过载。
我们还将在请求和最大页数之间添加 7 秒钟的延迟5个以避免猫cat API重载,因为那将是灾难性的。
function fetchCatPics({ limit, page, done }) { return homebrewFetch(`https://api.thecatapi.com/v1/images/search?limit=${limit}&page=${page}&order=DESC`) .then(body => ({ value: body, done })); } function catPics({ limit }) { return { [Symbol.asyncIterator]: async function*() { let currentPage = 0; // Stop after 5 pages while(currentPage < 5) { try { const cats = await fetchCatPics({ currentPage, limit, done: false }); console.log(`Fetched ${limit} cats`); yield cats; currentPage ++; } catch(error) { console.log('There has been an error fetching all the cats!'); console.log(error); } } } }; } (async function() { try { for await (let catPicPage of catPics({ limit: 10 })) { console.log(catPicPage); // Wait for 7 seconds between requests await new Promise(resolve => setTimeout(resolve, 7000)); } } catch(error) { console.log(error); } })()
这样,我们就会每隔 7 秒钟自动取回一整页的猫图片,以供欣赏。
一种更常见的在页面之间导航的方法是实现next和previous方法并将它们公开为控件:
function actualCatPics({ limit }) { return { [Symbol.asyncIterator]: () => { let page = 0; return { next: function() { page++; return fetchCatPics({ page, limit, done: false }); }, previous: function() { if (page > 0) { page--; return fetchCatPics({ page, limit, done: false }); } return fetchCatPics({ page: 0, limit, done: true }); } } } }; } try { const someCatPics = actualCatPics({ limit: 5 }); const { next, previous } = someCatPics[Symbol.asyncIterator](); next().then(console.log); next().then(console.log); previous().then(console.log); } catch(error) { console.log(error); }
如你所见,当你要获取数据页面或在应用程序的 UI 上进行无限滚动等操作时,异步迭代器将会非常有用。
这些功能已经在浏览器中使用了一段时间,在 Chrome v63+、 Firefox v57+ 和 Safari v11.1 中可用。但是当前在 IE 和 Edge 中不可用。
加载全部内容