首页 > 怎样理解js回调函数就是异步的?

怎样理解js回调函数就是异步的?

为什么第二种就是异步编程,A执行完>B>C>D>E,
我一直认为A>C>E>B>D,混乱的顺序(不按代码的前后顺序)才属于异步。
但第一种,第二种都是A>B>C>D>E依次执行的啊?

//第一种
A();
B();
C();
D();
E();
//第二种
A(function(){
    B(function(){
        C(function(){
            D(function(){
                E();
            });
        });
    });
});

哪里才是异步图中给你高亮了。接受 callback 的不一定是异步,callback 是为了在异步调用后接处理。


js 的执行引擎分两个序列, 你大部分代码是执行在ui线程里边的, 还有一个是事件队列(onclick, ajax, setTimeout, setInterval)都会在事件队列里执行,这部分代码才是异步的。你上边的代码全是同步的 之所以会出现那种顺序是因为执行的时候最里边的函数被放在了调用堆栈的最上面。


回调可以是同步的

function a(fn) {
  fn();
};

a(funtion() {
  console.log('I am callback, but not async'); // step1
});
console.log(end);// step2

实际情况中 为了避免程序傻傻等待ajax的返回,所以做成了异步的。

当然回调的功能远远不止这个, 还可以当作某种方法的抽象来使用。(表达能力有限,直接举个例子)

http.createServer(function(req, res) {
  //这里的req, res 是框架传进来的,我们只要好好的利用这两个参数即可,不用关心这个东西怎么来的
  //怎么说呢。。。就是。。。就是,nodejs事先写好了剧本,然后我们只要按着剧本演戏就好啦!
});

//再来个例子 这次自导自演!
function es(arr, fn) { //整个es函数就是剧本
  for (var i = 0, i < arr.length; i++) {
     fn(arr[i], i, arr); //导演通过回调函数交给演员三样东西
  }
}
es(arr, function(el, i, arr) {
  //演员在回调函数里拿到导演给的三要素,然后自由发挥满足导演!
  if (el === '人渣') el = '';//清除人渣
});

标题“回调函数就是异步的”不对。使用回调也不一定是异步。应该说异步中需要保证执行顺序,需要回调函数(这里不说promise等其他处理异步的方式)。

异步是非阻塞,是指不用等该任务返回就可以接着往后走。比如:

function asyncTask(taskDescription) {
    console.log('before ' + taskDescription);
    setTimeout(function() {
        console.log('this is an async task: ' + taskDescription);
    }, Math.random() * 100);
    console.log('after' + taskDescription);
}

asynTask(); // output: 'before timeout', 'after timeout', 'this is an async task'

那么假如多个异步函数放在一起:

asyncTask('task a');
asyncTask('task b');
asyncTask('task c');

这里的"this is an task: **"的输出不能确定,可能是a b c,可能是b a c...

你为什么会写出第二种写法,因为异步函数要保持顺序执行,需要知道异步任务什么时候结束:

function asyncTask(taskDescription, callback) {
    setTimeout(function() {
        console.log('complete an async task: ' + taskDescription);
        callback && callback(); //这里明确知道了task结束了
    }, Math.random() * 100);
}

这样子的把回调函数传进去,就可以保证异步任务的顺序执行:

asyncTask('task a', function() {
    asyncTask('task b', function() {
        asyncTask('task c');
    });
});

这里的输出肯定就是a b c了


为什么你认为第2中一定是异步编程呢
同步编程是指一个代码块中语句顺序执行,每个语句的执行结果在语句执行完毕后是可以马上得到预期的结果
而异步编程是指虽然语句被顺序执行了,但是期望的实际执行结果并不能在当下得到~~~

就你提供的例子
//第一种

A();
B();
C();
D();
E();

A/B/C/D/E函数将按代码的书写顺序顺序执行,但并不是说各个函数的内部代码的执行顺序和调用顺序相同。要具体看各个函数的实现

例如

function A(){
 setTimeout(function(){
   console.log('~~A~~');
  },1000);
}

function B(){
 setTimeout(function(){
   console.log('~~B~~');
  },100);
}

function C(){
 setTimeout(function(){
   console.log('~~C~~');
  },2000);
}

function D(){
    console.log('~~D~~');
}

function E(){
    console.log('~~E~~');
}

A();
B();
C();
D();
E();
//输出结果为 ~~D~~/~~E~~/~~B~~/~~A~~/~~C~~

//第二种

A(function(){
    B(function(){
        C(function(){
            D(function(){
                E();
            });
        });
    });
});

而第2种函数是一个回调函数的方式,各个函数接收一个函数对象作为执行参数供函数执行,看起来像是一个异步调用,但是其执行顺序也要看函数的实现方式,完全可能实现为一个同步调用方式
例如

function A(fun){
    fun();
    console.log('~~A~~');
}

function B(fun){
    setTimeout(fun,1000);
    console.log('~~B~~');
}

function C(func){
   func();
   console.log('~~C~~');
}

function D(func){
    func();
    console.log('~~D~~');
}

function E(){
     console.log('~~E~~');
}

A(function(){
    B(function(){
        C(function(){
            D(function(){
                E();
            });
        });
    });
});

//输出的结果为 ~~B~~/~~A~~/~~E~~/~~D~~/~~D~~/~~C~~

不能光看调用的形式,要看实际调用代码的实现方式


js是单线程,回调当然是异步了,这个跟你举例的A>C>D>B>E顺序似乎关系不大啊,ABCDE只是个名称,按照单线程的顺序来,谁在前面先执行谁,有回调的再插进去执行,这当然是异步了


回调!==异步.异步也不一定非要用回调。
异步是执行顺序,而不是代码写法。


异步的代码执行相对于顺序执行来说,最大的区别就是,异步的代码具有结果不确定性,即有不可再现性。

【热门文章】
【热门文章】