为什么不能stream行的JavaScript运行时处理同步的asynchronous脚本?
正如牛仔在这里的评论中所说的 ,我们都希望“用类似于这样的风格编写[非阻塞JavaScript]asynchronous代码:
try { var foo = getSomething(); // async call that would normally block var bar = doSomething(foo); console.log(bar); } catch (error) { console.error(error); }
“
所以人们已经想出了解决这个问题的方法
- callback库(例如asynchronous )
- 许诺
- 事件模式
- 精简
- 域和
- 发电机 。
但是这些都不会像上面的同步代码那样简单易懂。
那么为什么JavaScript编译器/解释器不能阻止我们当前所知的“阻塞”语句呢? 那么,为什么JavaScript编译器/解释器不能处理上面的同步语法,如果我们把它写成asynchronous样式?
例如,在处理上面的getSomething()
,编译器/解释器可能只是说“这个语句是对[文件系统/networking资源/ …]的调用,所以我会记getSomething()
听从那个调用的响应并在此同时继续我的事件循环“。 当调用返回时,执行可以继续执行doSomething()
。
您仍然会保留stream行的JavaScript运行时环境的所有基本function
- 单线程
- 事件循环
- 阻塞操作(I / O,networking,等待定时器)“asynchronous”处理
这只是对语法的一种调整,只要IT检测到一个asynchronous操作,解释器就可以暂停执行任何给定的代码,而不需要callback,代码只是在asynchronous调用后继续执行回报。
正如杰里米所说
JavaScript运行时没有任何事情会抢先暂停给定任务的执行,允许其他代码执行一段时间,然后恢复原来的任务
为什么不? (如在“为什么不能存在?”我对历史课不感兴趣)
为什么开发人员不得不关心一个语句是否阻塞? 计算机用于自动化人类所不擅长的东西(例如,编写非阻塞代码)。
你也许可以用它来实现它
- 像
的声明"use noblock"
;(有点像"use strict";
)打开这个“模式”的整个代码页。 编辑:"use noblock"
; 是一个不错的select,并误导了一些回答者,我试图改变JavaScript运行时的普通性质。 像'use syncsyntax';
可能会更好地描述它。 - 某种
parallel(fn, fn, ...);
语句允许您在"use syncsyntax"
同时并行运行; 模式 – 例如允许同时启动多个asynchronous活动 - 编辑:一个简单的同步风格的语法
wait()
,这将被用来代替setTimeout()
在"use syncsyntax"
; 模式
编辑:
作为一个例子,而不是写(标准callback版本)
function fnInsertDB(myString, fnNextTask) { fnDAL('insert into tbl (field) values (' + myString + ');', function(recordID) { fnNextTask(recordID); }); } fnInsertDB('stuff', fnDeleteDB);
你可以写
'use syncsyntax'; function fnInsertDB(myString) { return fnDAL('insert into tbl (field) values (' + myString ');'); // returns recordID } var recordID = fnInsertDB('stuff'); fnDeleteDB(recordID);
syncsyntax
版本的处理方式与标准版本完全相同,但是理解程序员的意图要容易得多(只要您明白syncsyntax
暂停执行此代码即可)。
为什么不? 没有理由,这只是没有完成。
而在2017年,这已经在ES2017中完成了: async
函数可以使用await
,无阻塞,为承诺的结果。 如果getSomething
返回一个promise(请注意await
),并且这是在一个async
函数中,你可以这样写代码:
try { var foo = await getSomething(); var bar = doSomething(foo); console.log(bar); } catch (error) { console.error(error); }
(我假设你只是希望getSomething
是asynchronous的,但它们都可以。)
现场示例(需要最新的浏览器,如最近的Chrome):
function getSomething() { return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() < 0.5) { reject(new Error("failed")); } else { resolve(Math.floor(Math.random() * 100)); } }, 200); }); } function doSomething(x) { return x * 2; } (async () => { try { var foo = await getSomething(); console.log("foo:", foo); var bar = doSomething(foo); console.log("bar:", bar); } catch (error) { console.error(error); } })();
The first promise fails half the time, so click Run repeatedly to see both failure and success.
那么为什么JavaScript编译器/解释器不能阻止我们当前所知的“阻塞”语句呢?
由于并发控制 。 我们希望它们被阻塞,所以(在JavaScript的单线程本质中),我们可以从竞争条件中安全地改变我们的函数的状态,而我们仍然在执行它。 我们不能有一个解释器,暂停执行当前函数的任何语句/expression式,并从程序的不同部分恢复。
例:
function Bank() { this.savings = 0; } Bank.prototype.transfer = function(howMuch) { var savings = this.savings; this.savings = savings + +howMuch(); // we expect `howMuch()` to be blocking }
同步码:
var bank = new Bank(); setTimeout(function() { bank.transfer(prompt); // Enter 5 alert(bank.savings); // 5 }, 0); setTimeout(function() { bank.transfer(prompt); // Enter 3 alert(bank.savings); // 8 }, 100);
asynchronous,任意无阻塞的代码:
function guiPrompt() { "use noblock"; // open form // wait for user input // close form return input; } var bank = new Bank(); setTimeout(function() { bank.transfer(guiPrompt); // Enter 5 alert(bank.savings); // 5 }, 0); setTimeout(function() { bank.transfer(guiPrompt); // Enter 3 alert(bank.savings); // 3 // WTF?! }, 100);
JavaScript运行时没有任何事情会抢先暂停给定任务的执行,允许其他代码执行一段时间,然后恢复原来的任务
为什么不?
为了简单和安全,请参阅上文。 (而且,历史课:就是这样完成的)
但是,这不再是事实。 有了ES6生成器,有一些东西可以让你显式地暂停当前函数生成器的执行: yield
关键字。
随着语言的发展,还有async
和await
ES7计划的关键字。
生成器[…不要…]导致代码与上面的同步代码一样简单易懂。
但他们呢! 在这篇文章中甚至是正确的:
suspend(function* () { // ^ "use noblock" - this "function" doesn't run continuously try { var foo = yield getSomething(); // ^^^^^ async call that does not block the thread var bar = doSomething(foo); console.log(bar); } catch (error) { console.error(error); } })
这里也有关于这个主题的很好的文章: http : //howtonode.org/generators-vs-fibers
因为Javascript解释器是单线程的,事件驱动。 这是最初的语言是如何开发的。
你不能做"use noblock"
因为在这个阶段没有其他工作可以发生。 这意味着你的用户界面不会更新。 您无法响应用户的鼠标或其他input事件。 你不能重画屏幕。 没有。
所以你想知道为什么? 因为JavaScript可能会导致显示更改。 如果你能同时做到这一点,那么你的代码和显示器就会有所有这些可怕的竞争条件。 你可能会认为你已经在屏幕上移动了一些东西,但是它没有绘制,或者绘制了,并且在绘制之后移动了它,现在需要再次绘制等。这种asynchronous性质允许对于执行中的任何给定事件堆栈有一个已知的良好状态 – 没有什么会修改正在执行的数据。
这并不是说某种forms就不存在你想要的东西。
async
库允许你做一些事情,比如你的parallel
思想(等等)。
生成器/asynchronous/等待将允许你编写看起来像你想要的代码(尽pipe它本质上是asynchronous的)。
虽然你在这里做出一个错误的声明 – 人类在编写asynchronous代码方面不错。
另外的答案讲了multithreading和并行性引入的问题。 但是,我想直接解答您的答案。
为什么不? (如在“为什么不能存在?”我对历史课不感兴趣)
绝对没有理由。 ECMAScript – JavaScript规范没有提及并发性,没有指定运行的代码,它没有指定事件循环或事件,也没有指定任何有关阻塞或阻塞的内容。
并发性在JavaScript中的工作方式是由其宿主环境定义的 – 例如在浏览器中,这就是DOM,DOM指定事件循环的语义。 像setTimeout
这样的“async”函数只是DOM而不是JavaScript语言的关注点。
而且没有什么说JavaScript运行时必须运行单线程等等。 如果您有顺序代码,则会指定执行顺序,但是没有任何东西可以阻止任何人在multithreading环境中embeddedJavaScript语言。