有没有办法执行不冻结浏览器的同步 AJAX 查询?在我看来,同步请求在大多数情况下更容易处理,但是它们阻止代码的其他部分执行的事实是一个真正的杀手。有没有办法在没有负面影响的情况下获得同步 AJAX?(是的,我意识到“同步 AJAX”这个词是矛盾的。)
5 回答
我将提供一个例子来说明允许这种行为的不良影响。
假设你有这个程序:
<script>
var file = "foo.json";
function nullIt() {
file = null;
}
function loadFile() {
if (file != null) {
synchronousLoad(file);//imagine the load takes 5 seconds
alert("i just loaded: " + file);
}
}
window.onload = loadFile;
</script>
<button onclick="nullIt()">click me</button>
这里的坏事-
- 在
synchronousLoad()
阻塞 5 秒时,用户单击按钮,事件处理程序快速运行完成。 - 现在
file
变量为空。 synchronousLoad()
完成并返回,让下一行代码继续执行- 但
file
现在为空,并且向用户输出的消息已损坏。
这里真正的问题是你不能再以同样的方式来推理你的代码了。仅仅因为某些事实在第 5 行是正确的,并不意味着它在下一行仍然正确。这使得编写无错误的程序变得非常困难。
一些编程语言支持多线程,你必须处理这些问题,尽管你有工具可以帮助处理这些问题。但是,程序员方面仍然需要付出很多额外的努力。
比较而言,使用回调做异步操作是ez-mode。
不,根据定义,同步是阻塞的。在该过程完成之前,任何事情都无法进行。这包括 Web 浏览器中 UI 的其余部分。
它应该是异步的,因此最好的方法是将代码设计为异步工作。
在即将发布的 ECMAScript 2016 (ES7) 标准中,有一组新的语言关键字旨在执行与您似乎正在寻找的非常相似的事情,称为async
和await
.
这些关键字不允许“非阻塞同步 AJAX”,但它们确实允许您以看起来同步的方式编写异步代码。这是一个简单的例子:
// Let's say you have an asynchronous function that you want to call in a synchronous
// style...
function delayedEval(delay, func) {
// First, ensure that the function returns a Promise object. If it doesn't, wrap it with
// another function that does.
return new Promise((resolve, reject) => {
setTimeout(() => resolve(func()), delay)
})
// For more on Promises, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
}
// Then, declare a function as asynchronous. This causes it to return a Promise that
// resolves to its return value, instead of returning its return value directly.
async function delayedHello() {
// Inside an async function, you can call other async functions like this, which looks
// very much like a synchronous call (even though it isn't).
let message = await delayedEval(1500, () => "Hello, world!")
console.log(message)
}
// This returns (a Promise) immediately, but doesn't print "Hello, world!" until 1.5
// seconds later. (At which point it resolves the Promise.)
delayedHello()
基本上,不是“没有负面影响的同步 AJAX”,而是async
让await
您获得没有所有负面影响的异步AJAX 。(杂乱的代码,有很多处理回调的逻辑。)
async
并且await
是ES7 标准中“异步函数”候选推荐的一部分。
我不认为这是可能的。您可以使用异步方法并使用 AJAX 完成事件,而不是尝试使用同步查询。
使用 jQuery.ajax() 的示例
jQuery.ajax({
url: "URL HERE",
data: "whatever you are sending"
}).done(function (data) {
// Do things with data
});
因此,您可以使用异步请求并在请求完成后执行代码,而不是使用同步请求。因此,它不会冻结您的浏览器。
由于此处其他答案中概述的原因,无法在不阻塞浏览器中的其他事件的情况下执行同步 AJAX 调用。
但是,如果代码复杂性是您希望避免异步方法调用的主要原因,您可能会对 Javascript 交叉编译器streamline.js感兴趣,它允许您编写异步方法调用,就好像它们是同步的一样,并获得相同的结果就好像您异步编写了调用一样。
从项目的 GitHub 页面:
streamline.js 是一种用于简化异步 Javascript 编程的语言工具。
而不是编写像这样的毛茸茸的代码:
function archiveOrders(date, cb) { db.connect(function(err, conn) { if (err) return cb(err); conn.query("select * from orders where date < ?", [date], function(err, orders) { if (err) return cb(err); helper.each(orders, function(order, next) { conn.execute("insert into archivedOrders ...", [order.id, ...], function(err) { if (err) return cb(err); conn.execute("delete from orders where id=?", [order.id], function(err) { if (err) return cb(err); next(); }); }); }, function() { console.log("orders have been archived"); cb(); }); }); }); }
你写:
function archiveOrders(date, _) { var conn = db.connect(_); conn.query("select * from orders where date < ?", [date], _).forEach_(_, function(_, order) { conn.execute("insert into archivedOrders ...", [order.id, ...], _); conn.execute("delete from orders where id=?", [order.id], _); }); console.log("orders have been archived"); }
和流线型转换代码并处理回调!
无需学习控制流 API!你只需要遵循一个简单的规则:
用下划线替换所有回调并编写代码,就好像所有函数都是同步的一样。
有关 streamline.js 的更多信息,请阅读博文Asynchronous Javascript – the tale of Harry。