回调函数的思想
回调函数的思想就是异步编程的思想,js语言中异步设计思想的直接体现就是回调函数。
同步设计思想
//同步思想:先在业务函数中调用createData工具函数产生了业务数据以后,再使用产生的业务数据
function createData(){
//工具函数:将产生的业务数据直接返回
return Math.ceil(Math.random()*(7-(-3)+(-3)));
}
let obj={
data:{name:"AOTU"},
tool(){
//业务函数:使用产生的业务数据
let data=createData();
this.data=data;
//先执行createData()后,将返回的业务数据赋值给data变量,然后才使用这个业务数据
}
};
异步设计思想
//异步设计思想:createData工具函数就是用于产生了业务数据以后不是直接返回后在使用
//而是将产生的业务数据传给回调函数所以传入的回调函数就是用于执行业务的函数,在createData工具函数中内部调用
//首先在createData工具函数内部中先执行createData工具函数自己的业务代码就是产生业务数据
//然后在createData工具函数内部中调用传入的执行业务的回调函数,再把产生的业务数据传给回调函数使用
let obj={
data:{name:"AOTU"},
tool(){
createData((n)=>{
//n就是工具函数产生的业务数据
this.data=n;//用于执行业务的代码:将产生的业务数据设置给obj对象的data属性
});
}
};
function createData(callback){
//产生的业务数据
let re=Math.ceil(Math.random()*(7-(-3)+(-3)));
//将产生的业务数据传给回调函数使用
callback(re);
};
console.log(obj.data);
obj.tool();
console.log(obj.data);
回调函数的调用是在工具函数运行完自己的业务代码后就产生了业务数据,然后才是在工具函数内部调用传入的回调函数把产生的业务数据传给回调函数执行业务代码。
异步编程的思想
同步函数
js代码是单线程语言其业务代码需要一个一个的运行不能同时运行,所以js代码写出的function函数永远是同步函数,会阻塞代码的运行。则以下fn函数不是异步函数是同步函数
console.log(1);//是一个任务
fn();//fn函数调用也是一个任务
console.log(2);//是一个任务
function fn(){
for(let i=0;i<1000;i++){};
console.log(3);
};
则控制台先打印1,然后是调用fn函数,fn函数中会先等待for循环运行完再运行console.log(3)就是控制台才打印3。等待fn函数运行完后fn函数调用代码才运行完,才会执行console.log(2)语句,最后控制台才打印2。
异步函数
异步阻塞函数
function fn(callback,n){
for(let j=0;j<n;j++){};
callback();
};
console.log(1);
fn(()=>{console.log(2);},10000);
//fn函数中的回调函数就是异步阻塞函数
//因为需要等fn函数中大括号内部的代码运行完后才会运行fn函数下面的代码,fn函数大括号内部中的函数是js代码实现的只能单线程
console.log(3);
先运行console.log(1)所以控制台先打印1,然后运行调用同步函数fn的代码,同步fn函数中先运行完for循环,再运行调用回调函数的代码,运行回调函数中的console.log(2),然后控制台才打印2,等同步fn函数运行完后调用同步函数fn的代码才运行完毕,最后运行console.log(3),所以控制台最后打印3。
异步非阻塞函数
js代码不能多线程,但是js底层代码可以实现多线程。因为运行js代码的环境(js引擎)是由C/C++语言实现的,就可以多线程运行代码。
底层C/C++可以封装不阻塞代码运行的异步函数,让上层js代码调用。比如:setTimeout()计时器函数和promise对象调用的then函数等,这些函数大括号内部的代码是底层语言设计的,可以多线程。
setTimeout(()=>{console.log(1);},1000);//此时setTimeout()的业务是等1000毫秒后再调用回调函数
//setTimeout()的业务就是在函数的大括号中,是由底层C/C++语言实现的
console.log(2);
虽然setTimeout()任务先开启,但是底层运行环境会再开启另一个新的线程去运行setTimeout中大括号的代码则setTimeout中大括号的代码和console.log(2)语句是同时进行的。所以控制台会先打印2,然后等setTimeout中大括号的代码运行完后,才会调用回调函数,运行回调函数的代码,控制台最后打印1。
异步非阻塞函数中谁先回到调用回调函数,回调函数就谁先云行。