函数的运行时执行过程

时间:2019-01-07 18:24:46

标签: javascript events runtime callstack

我想知道下面的代码如何在运行时执行。 我知道事件是由浏览器API处理的,因此会从调用堆栈中弹出。然后,回调由API在队列中注册。一旦堆栈为空,这些消息就会执行。

但是当onclick回调函数中有多个函数时,会发生这种情况。同时具有顺序和异步功能。然后他们会再次从“队列以调用”堆栈推回浏览器API,然后再次推回到浏览器API吗?

下面的整个代码如何执行?

$('#test').on(click, function() {
   console.log('start');

   //modifies the dom like add div in the html
   modifyDom();

   //http ajax call
   someAjaxCall();

   while(let i < 5) {
      console.log(i);
      i++
   }

   setTimeout(function(){ console.log('Zero Delay'); },0);


});

3 个答案:

答案 0 :(得分:0)

让我们添加行号以跟踪执行情况

01 $('#test').on(click, function() {
02    console.log('start');
03 
04    modifyDom();
05 
06    someAjaxCall();
07 
08    while(let i < 5) {
09       console.log(i);
10       i++;
11    }
12 
13    setTimeout(
14       function(){
15          console.log('Zero Delay');
16       },
17       0
18    );
19 });
  1. 01:附加事件处理程序
  2. 等待点击 ...
  3. 点击
  4. 02:在控制台上打印“开始”
  5. 04:执行DOM操作
  6. 06:调度ajax请求并等待网络线程(不是主线程)上的响应
  7. 08-11
    1. i为0
    2. i是1
    3. i是2
    4. i是3
    5. i是4
  8. 13-18:向计时器线程(而非主线程)添加回调14-16
  9. 等待线程返回控制权...

现在,可能发生以下任何情况:

  1. 在完成之前的执行之前,ajax和/或超时会“返回”
  2. 超时前ajax“返回”
  3. ajax之前的超时“返回”

在情况1中,主线程根本不在乎“返回”什么,因为它是单个连续的同步指令堆栈。这意味着案例1最终解决了案例2和3

在第2种和第3种情况下,由于promise微任务的优先级高于计时器线程,因此首先应承诺ajax“ return”,然后发生超时回调


编辑1

强烈建议观看this talk关于事件循环基础知识的内容,因为它解释了同步代码,计时器,承诺和更多内容的相互作用

答案 1 :(得分:0)

您所说的关于调用堆栈和队列的说法是正确的。 JavaScript将执行所有同步代码,并将异步调用添加到队列中。 setTimeout声明何时可以预先执行。如果队列中没有等待任务,则将在提供的最短时间(在这种情况下为零)中清除调用堆栈后,将调用该任务。 setTimeout的delay参数不能保证将在那个时候执行,但是它是它可以执行时的最小值。这是因为队列中的现有项目必须先清除,然后才能处理。

网络调用的事件处理程序(例如someAjaxCall函数)也是回调。 Ajax调用的侦听器基本上处于待处理状态,直到相关事件发生(例如progresserrorsuccess)之后才添加到队列中。它们仍然与setTimeout完全相同,并且会通过队列触发回调。

asyncawait语法可能会帮助您解决这一问题。通过声明函数异步,您可以更好地处理工作流中的异步任务。例如,如果您需要在继续之前处理Ajax调用,则可以执行以下操作:

// declaring function with async tag
$('#test').on(click, async function() {
   console.log('start');

   //modifies the dom like add div in the html
   modifyDom();

   //http ajax call
   try {
       let response = await someAjaxCall(); 
       console.log('data!', response);
   } catch(err){
       console.log('network error!', err);
   }


   while(let i < 5) {
      console.log(i);
      i++
   }

   setTimeout(function(){ console.log('Zero Delay'); },0);


});

function someAjaxCall(){
    return new Promise((resolve, reject) => {
       $.ajax({
           url:'someUrl',
           type:'GET',
           success:function(response){
               resolve(response); 
           },
           error:function(err){
               reject(err);
           }
       })
    })
}

在此示例中,while函数在try / catch块中等待someAjaxCall的响应。该功能的其余部分的执行被延迟,直到该功能解决或拒绝为止。使用此设置,您将看到以下注销信息:

// start
// either 'data!', with the {response} or 'network error!', with the {error}
// 1
// 2
// 3
// 4
// 5
/*
    call stack has ended
    the function will return undefined because there is nothing being returned
    all queues are assessed from oldest to youngest.
    only the setTimeout is in the queue so it's callback is executed
*/
//'Zero Delay'

更新

另外,如果您需要测量函数执行所需的时间,则可以使用console.timeconsole.timeEnd(文档here)。通过在函数的开头添加带有标签的console.time,例如console.time('Click time'),可以在其他位置添加console.timeEnd('Click time'),以查看在这些点之间准确运行了多长时间(例如单击功能的开始,直到setTimeout的回调为止。

答案 2 :(得分:-2)

点击#test后,您将拥有:

  1. console.log('start');进入回调后直接执行
  2. modifyDom()是下一个。它也是同步执行的。
  3. someAjaxCall();。在这里,请求将直接发送,然后它自己的回调将在最终收到请求时(异步)处理响应
  4. while loop同步运行
  5. setTimeout尽管它通常异步运行(在一定的超时后),但您已将延迟分配为0。因此它将在调用后立即运行。