为什么现在我的未来价值不可用?
我的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_milk
是asynchronous的 –这是在未来某个时间不会产生牛奶的东西。 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); }