为什么不能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关键字。

随着语言的发展,还有asyncawait 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语言。