ECMAScript 6 (又名ECMAScript 2015或ES6)为JavaScript带来了许多新功能,这将使该语言非常适合大型应用程序。 这些功能之一是更好地支持使用Promise和Generator进行的异步编程。 另一个是Fetch API的添加,该API旨在取代XMLHttpRequest
作为与远程资源进行通信的基础。
Fetch API的方法返回ES6 Promise
对象,该对象可与生成器结合使用以形成复杂的异步操作的基础。 从一系列异步操作(每个操作都取决于上一个操作返回的值)到必须反复对服务器进行一次异步调用以获取最新更新的操作,这可以是任何东西。
在本文中,我们将了解如何将Fetch API与生成器结合使用以构建异步API。 Chrome,Opera,Firefox和Android浏览器目前支持Fetch API。 GitHub提供了针对不支持的浏览器的polyfill 。
与以往一样,可以在我们的GitHub存储库中找到本文的代码,并且在文章底部有最终技术的演示 。
提示:如果需要有关什么是生成器以及它们如何工作的复习,请查看: ECMAScript 2015:生成器和迭代器
那么我们如何使用生成器来执行异步操作呢? 好吧,如果我们分析生成器的工作方式,我们将找到答案。
实现迭代器的生成器函数具有以下结构:
function *myIterator(){
while(condition){
//calculate next value to return
yield value;
}
}
yield
关键字负责返回结果并中止迭代器函数的执行,直到下次调用它为止。 它还保留了函数的状态,而不是在下次调用它时重新运行所有内容,从而有效地记住了该函数的上次中断位置。
我们可以重新构想上述功能,而无需使用while循环,如下所示:
function *myIterator(){
//calculate value 1
yield value1;
//calculate value 2
yield value2;
...
//calculate value n
yield valuen;
}
在上述两种情况下,该功能的行为均相同。 使用yield关键字的唯一原因是暂停函数的执行,直到下一次迭代为止(这本身似乎是异步的)。 由于yield
语句可以返回任何值,所以我们还可以返回promise并使函数运行多个异步调用。
提示:有关Fetch API的更新,请查看: Fetch API简介
如前所述,Fetch API旨在替代XMLHttpRequest
。 这个新的API提供了对HTTP请求的每个部分的控制,并返回一个基于服务器响应来解决或拒绝的Promise。
可以将Fetch API和生成器一起使用的用例之一是长轮询 。 长轮询是一种客户端不断向服务器发送请求直到获得响应的技术。 在这种情况下,可以使用生成器来不断产生响应,直到响应包含数据为止。
为了模仿长时间轮询,我在示例代码中包含了Express REST API ,该API在五次尝试后都会响应城市的天气信息。 以下是REST API:
var polls=0;
app.get('/api/currentWeather', function(request, response){
console.log(polls, polls<5);
if(polls < 5){
console.log("sending...empty");
polls++;
response.send({});
}
else{
console.log("sending...object");
response.send({
temperature: 25,
sky: "Partly cloudy",
humid: true
});
polls = 0;
}
});
现在,让我们编写一个生成器函数,该函数多次调用此API,并在每次迭代时返回一个Promise。 在客户端,我们不知道经过多少次迭代后就会从服务器获取数据。 因此,此方法将在每次迭代中对服务器执行ping操作并在每种情况下返回promise的无限循环。 以下是此方法的实现:
function *pollForWeatherInfo(){
while(true){
yield fetch('/api/currentWeather',{
method: 'get'
}).then(function(d){
var json = d.json();
return json;
});
}
}
我们需要一个函数来继续调用该函数,并在promise解析后检查该值是否存在。 这将是一个递归函数,它将调用生成器的下一个迭代,并且仅在找到从生成器返回的值时才停止该过程。 以下代码段显示了此方法的实现以及一个调用此方法的语句:
function runPolling(generator){
if(!generator){
generator = pollForWeatherInfo();
}
var p = generator.next();
p.value.then(function(d){
if(!d.temperature){
runPolling(generator);
} else {
console.log(d);
}
});
}
runPolling();
如我们在这里看到的,对函数runPolling
的第一次调用将创建generator
对象。 next
方法返回一个具有value
属性的对象,在我们的例子中,该对象包含由fetch
方法返回的promise。 此承诺解决后,它将包含一个空对象(如果polls
变量小于5则返回),或者包含一个包含所需信息的对象。
接下来,我们检查该对象的temperature
属性(这将指示成功)。 如果不存在,我们将generator
对象传递回下一个函数调用(以免丢失生成器的状态),或者将对象的值打印到控制台。
要查看实际效果,请从我们的仓库中获取代码,安装依赖项,启动服务器,然后导航至http:// localhost:8000 。 您应该在shell中看到以下结果:
0 true
sending...empty
1 true
sending...empty
2 true
sending...empty
3 true
sending...empty
4 true
sending...empty
5 false
sending...object
并且对象本身已登录到浏览器控制台。
通常,我们需要实现多个相关的异步调用,其中每个连续的异步操作取决于前面的异步操作返回的值。 如果我们有一组这样的操作,并且必须多次调用它们,则可以将它们放到一个生成器函数中,并在需要时执行它。
为了演示这一点,我将使用GitHub的API 。 该API使我们可以访问有关用户,组织和存储库的基本信息。 我们将使用此API来获取组织随机回购的贡献者列表,并在屏幕上显示获取的数据。
为此,我们需要调用三个不同的端点。 这些是要执行的任务:
让我们围绕Fetch API创建包装函数,以避免重复代码以创建标头和构建请求对象。
function wrapperOnFetch(url){
var headers = new Headers();
headers.append('Accept', 'application/vnd.github.v3+json');
var request = new Request(url, {headers: headers});
return fetch(request).then(function(res){
return res.json();
});
}
以下函数使用上述函数,并为每次调用产生一个承诺:
function* gitHubDetails(orgName) {
var baseUrl = "https://api.github.com/orgs/";
var url = baseUrl + orgName;
var reposUrl = yield wrapperOnFetch(url);
var repoFullName = yield wrapperOnFetch(reposUrl);
yield wrapperOnFetch(`https://api.github.com/repos/${repoFullName}/contributors`);
}
现在,让我们编写一段逻辑来调用上述函数以获取生成器,然后使用从服务器获取的值来填充UI。 每次对生成器的next
方法的调用都返回一个承诺时,我们将不得不链接这些承诺。 以下是使用上述函数返回的生成器的代码框架:
var generator = gitHubDetails("aspnet");
generator.next().value.then(function (userData) {
//Update UI
return generator.next(userData.repos_url).value.then(function (reposData) {
return reposData;
});
}).then(function (reposData) {
//Update UI
return generator.next(reposData[randomIndex].full_name).value.then(function (selectedRepoCommits) {
//Update UI
});
});
要查看运行情况,如上所述,请从我们的仓库中获取代码,安装依赖项,启动服务器,然后导航至http:// localhost:8000 。 或者只是查看下面的演示(尝试重新运行)。
请参阅CodePen上使用SitePoint ( @SitePoint ) 的Fetch API进行笔的多个依赖异步调用 。
在本文中,我演示了如何将Fetch API与生成器结合使用以构建异步API。 ECMAScript 6将为该语言带来许多新功能,并且寻找将它们结合起来并利用其功能的创新方法通常可以带来出色的结果。 但是你觉得呢? 这是我们今天可以在我们的应用程序中开始使用的技术吗? 我很想听听您在评论中的想法。
From: https://www.sitepoint.com/asynchronous-apis-using-fetch-api-es6-generators/