什么是JavaScript的JavaScript函数的开销

whitzsjs  于 2024-01-05  发布在  Java
关注(0)|答案(3)|浏览(121)

问题:与常规函数的return语句相比,在引擎运行时将函数声明为async并最终声明为await是否有计算开销(如果有,在多大程度上)?

async function foo() {
    var x = await bar(); // <--- bar() is non-blocking so await to get the return value
    return x; // the return value is wrapped in a Promise because of async
}

字符串

function foo() {
    var x = bar(); // <--- bar() is blocking inside its body so we get the return value
    return new Promise(resolve => { resolve(x); }); // return a Promise manually
}

上下文

由于JavaScript(即Nodejs)采取的异步方向,为什么他们不认为每个函数默认都是异步的(根据async关键字)?
这样,人们可以决定将任何函数调用视为Promise并玩异步游戏,或者只是await
我认为函数体中的await-ing会产生堆叠本地函数作用域的开销,而当函数返回时,正常的事件循环会继续进行,并且不必将内部函数作用域推入堆栈?
这归结为一个额外的问题:在一个复杂的类的虚拟系统中,(在某个深度)需要一个同步IO操作(见注),这将是理想的await 'ed。只有当该方法被标记为async时才有可能。这反过来又要求调用函数是async,以便能够再次执行await。因此,在需要的时候,所有东西都标记为asyncawait..

注意:请不要争论不做任何同步操作的必要性,因为这不是重点。
注2:这个问题不是关于什么是awaitasync,也不是关于它何时执行的,而是关于性能和语言的内部结构(即使存在多个实现,概念也可能存在固有的语义开销)。

lhcgjxsq

lhcgjxsq1#

与同步函数相比,异步函数具有固有的开销。当然可以将所有内容都设置为异步函数,但您可能会很快遇到性能问题。

同步vs异步

函数返回一个值。
async函数创建一个Promise对象,并从函数返回。Promise对象的设置是为了维护异步任务的状态,并处理错误或后续的链式调用。Promise将在事件循环的下一个tick之后被解析或拒绝。(这有点简短,如果你想了解细节,请使用read the the spec)与简单的函数调用和返回值相比,这具有内存和处理开销。
量化开销是有点无用的,因为大多数Node.js函数都是Node.js函数,因为它们必须等待外部Node.js线程来完成一些工作,通常执行缓慢的IO。与操作的总时间相比,设置Promise的开销非常小,特别是如果替代方案是阻塞主JS线程。
另一方面,同步代码在主JS线程中立即运行。交叉区域是调度同步代码,用于定时或将主JS线程的使用“节流”到下一个tick,以便GC和其他JavaScript任务有机会运行。
如果你在一个紧循环中一个字符接一个字符地解析一个字符串,你可能不想创建一个promise并等待它在每次迭代中解析,因为完成这个过程所需的内存和时间会迅速增加。
另一方面,如果你的应用程序只做query a database,并将结果转储到koa http响应中,那么你可能会在一个python promise中做大多数事情(尽管下面仍然有很多同步函数来实现这一点)。

愚蠢的例子

一个人为示例的基准测试,同步返回和解决同一同步操作的各种方法之间的差异。

const Benchmark = require('benchmark')
const Bluebird = require('bluebird')

let a = 3
 
const asyncFn = async function asyncFn(){
  a = 3
  return a+2
}

const cb = function(cb){
  cb(null, true)
}
let suite = new Benchmark.Suite()
suite
  .add('fn', function() {
    a = 3
    return a+2
  })
  .add('cb', {
    defer: true,
    fn: function(deferred) {
      process.nextTick(()=> deferred.resolve(a+2))
    }
  })
  .add('async', {
    defer: true,
    fn: async function(deferred) {
      let res = await asyncFn()
      deferred.resolve(res)
    }
  }) 
  .add('promise', {
    defer: true,
    fn: function(deferred) {
      a = 3
      return Promise.resolve(a+2).then(res => deferred.resolve(res))
    }
  })
  .add('bluebird', {
    defer: true,
    fn: function(deferred) {
      a = 3
      return Bluebird.resolve(a+2).then(res => deferred.resolve(res))
    }
  })

  // add listeners
  .on('cycle', event => console.log("%s", event.target))
  .on('complete', function(){
    console.log('Fastest is ' + this.filter('fastest').map('name'))
  })
  .on('error', error => console.error(error))
  .run({ 'async': true })

字符串
运行

→ node promise_resolve.js
fn x 138,794,227 ops/sec ±1.10% (82 runs sampled)
cb x 3,973,527 ops/sec ±0.82% (79 runs sampled)
async x 2,263,856 ops/sec ±1.16% (79 runs sampled)
promise x 2,583,417 ops/sec ±1.09% (81 runs sampled)
bluebird x 3,633,338 ops/sec ±1.40% (76 runs sampled)
Fastest is fn


如果你想更详细地比较各种promise和callback实现的性能/开销,也可以检查bluebirds benchmarks

file                                       time(ms)  memory(MB)
callbacks-baseline.js                           154       33.87
callbacks-suguru03-neo-async-waterfall.js       227       46.11
promises-bluebird-generator.js                  282       41.63
promises-bluebird.js                            363       51.83
promises-cujojs-when.js                         497       63.98
promises-then-promise.js                        534       71.50
promises-tildeio-rsvp.js                        546       83.33
promises-lvivski-davy.js                        556       92.21
promises-ecmascript6-native.js                  632       98.77
generators-tj-co.js                             648       82.54
promises-ecmascript6-asyncawait.js              725      123.58
callbacks-caolan-async-waterfall.js             749      109.32

cedebl8k

cedebl8k2#

有一些操作是不需要等待的,例如,如果你想执行几个XHR,同时加载几个文件,自动等待会使加载过程线性化,这是不好的

twh00eeo

twh00eeo3#

Promises/await和Promises的优点在于你可以混合使用。对于多个XHR请求,你可以简单地返回Promise.all():

async function fetchPages() {
    return Promise.all([
        fetch("this.html"),
        fetch("that.html")
    ]);
}

for (var page of fetchPages() { ... }

字符串

相关问题