为什么现在我的未来价值不可用?

我的ajax调用不返回任何东西! 这是我的代码:

var answer; $.getJSON('/foo.json') . done(function(response) { answer = response.data; }); console.log(answer); 

即使networking调用成功,我可以看到响应包含数据,控制台日志“未定义”! 发生什么事?

我们发现自己处于一个似乎沿着我们所说的“时间”维度前进的宇宙。 我们不是真正了解什么时候,但是我们已经开发了抽象和词汇,让我们推理和谈论它:“过去”,“现在”,“未来”,“之前”,“之后”。

我们build立的计算机系统 – 越来越多 – 有时间作为一个重要的维度。 某些事情将来会发生。 那么在事情最终发生之后还有其他事情需要发生。 这是所谓的“asynchronous性”的基本概念。 在我们日益networking化的世界中,最常见的asynchronous情况是等待某个远程系统响应某个请求。

考虑一个例子。 你打电话给送牛奶,点一些牛奶。 当它来到,你想把它放进你的咖啡。 你现在不能把牛奶放在你的咖啡里,因为它还没到。 在将它放入咖啡之前,您必须先等待它。 换句话说,以下将不起作用:

 var milk = order_milk(); put_in_coffee(milk); 

因为JS没有办法知道在执行put_in_coffee之前需要等待 order_milk完成。 换句话说,它不知道order_milkasynchronous的 –这是在未来某个时间不会产生牛奶的东西。 JS和其他声明性语言,不需要等待一个又一个的执行。

这个问题的经典JS方法,利用JS支持函数作为可以传递的第一类对象这一事实,就是将一个函数作为parameter passing给asynchronous请求,然后在asynchronous请求完成时调用它它的任务在未来某个时候。 这是“callback”的方法。 它看起来像这样:

 order_milk(put_in_coffee); 

order_milk启动,命令牛奶,然后,当它只到达时,它调用put_in_coffee

这种callback方法的问题在于它污染了函数的正常语义,报告return结果; 相反,函数必须通过调用作为参数给定的callback来报告结果。 而且,这种方法在处理更长的事件序列时可能迅速变得笨拙。 例如,假设我想等待牛奶被放入咖啡,然后才进行第三步,即喝咖啡。 我最终需要写这样的东西:

 order_milk(function(milk) { put_in_coffee(milk, drink_coffee); } 

在这里,我将通过put_in_coffee这两个牛奶放进去,而且一旦牛奶被放入,这个动作( drink_coffee )就会执行。这样的代码变得很难写,读和debugging。

在这种情况下,我们可以将问题中的代码重写为:

 var answer; $.ajax('/foo.json') . done(function(response) { callback(response.data); }); function callback(data) { console.log(data); } 

input承诺

这就是“承诺”这个概念的动机,这是一种特定types的价值,代表某种未来asynchronous的结果。 它可以代表已经发生的事情,或者将来会发生的事情,或者可能根本不会发生。 承诺有一个单一的方法, then这个方法被命名,当承诺所代表的结果已经实现时,你传递一个行动来执行。

在我们的牛奶和咖啡的情况下,我们deviseorder_milk来返回到达牛奶的承诺,然后指定put_in_coffee作为一个动作,如下所示:

 order_milk() . then(put_in_coffee) 

这样做的一个优点是我们可以将这些string串起来创build未来事件序列(“链接”):

 order_milk() . then(put_in_coffee) . then(drink_coffee) 

让我们将承诺应用于您的特定问题。 我们将我们的请求逻辑封装在一个函数中,它返回一个promise:

 function get_data() { return $.ajax('/foo.json'); } 

实际上,我们所做的只是将$.ajax添加到$.ajax 。 这是有效的,因为jQuery的$.ajax已经返回了一种类似promise的东西。 (在实践中,我们不想深入细节,我们宁愿包装这个调用,以便返回一个真正的承诺,或者使用替代$.ajax替代方法)。现在,如果我们要加载文件并等待它完成然后做点什么,我们可以简单地说

 get_data() . then(do_something) 

例如,

 get_data() . then(function(data) { console.log(data); }); 

当使用promise时,我们最终会传入大量的函数,所以使用更紧凑的ES6风格的箭头函数通常是很有帮助的:

 get_data() . then(data => console.log(data)); 

async关键字

但是仍然有一些含糊不清的东西,如果是同步编写代码的话,如果是asynchronous的,那么就是一种完全不同的方式。 为了同步,我们写

 a(); b(); 

但如果a是asynchronous的,我们必须写

 a() . then(b); 

上面,我们说“JS没有办法知道它需要等待第一个调用完成之后才执行第二个”。 如果有什么方法可以告诉JS呢? 事实certificate,有 – await关键字,在一个称为“async”函数的特殊types的函数中使用。 这个function是即将到来的ES版本的一部分,但已经在诸如Babel这样的转换器中提供了正确的预设。 这使我们可以简单地写

 async function morning_routine() { var milk = await order_milk(); var coffee = await put_in_coffee(milk); await drink(coffee); } 

在你的情况下,你可以写一些类似的东西

 async function foo() { data = await get_data(); console.log(data); }