获取返回值
我们都知道同步API可以从返回值中拿到API执行的结果, 但是异步API却是不可以的
// 同步
function sum (n1, n2) {
return n1 + n2;
}
const result = sum (10, 20);//30
// 异步
function getMsg () {
setTimeout(function () {
return { msg: 'Hello Node.js' }
}, 2000);
}
const msg = getMsg ();
console.log(msg);//undefined
为什么会是undefined?
我们知道JavaScript的代码执行机制,会将异步代码放入任务队列中,而继续执行下面的同步代码,而造成undefined的原因正是异步函数还没有被触发,所以当调用getMsg ()函数时其中异步函数不阻塞线程,代码继续执行,getMsg ()函数默认return了一个undefined
所以这就造成了异步代码的不可控,我们根本不知道他什么时候执行,也就无法直接获取它的执行结果
那有办法解决么?
当然
回调函数获取异步API返回值
我们可以定义一个这样的函数:
function getData(callback) {
callback(123);
}
getData(function(n) {
console.log(n)//输出123
});
这就是回调函数
通过在函数形参中拿到的回调函数引用地址,可以调用回调函数并传递参数给回调函数的形参,这样回调函数就可以获得被调用函数中的信息
所以我们刚刚的代码可以写成这样
function getMsg (callback) {
setTimeout(function () {
callback({
msg: 'hello node.js'
})
}, 2000)
}
getMsg(function (data) {
console.log(data);
});
异步代码执行顺序
异步代码除了返回值还有一个执行顺序问题
console.log('代码开始执行');
setTimeout(() => {
console.log('2秒后执行的代码');
}, 2000);
setTimeout(() => {
console.log('"0秒"后执行的代码');
}, 0);
console.log('代码结束执行');
上面的代码输出结果是什么呢?
我们知道JavaScript的代码执行机制,来分析一下:
很明显在执行栈中遇到异步代码,就会将其放到异步代码执行区,这时两个定时器都被放到异步代码执行区中了,然后在异步代码执行区中先触发的异步代码会先一步被放入任务队列中,等待调用
显然输出结果是先输出“0秒"后执行的代码”,然后再输出’2秒后执行的代码’
这样不可控制的输出顺序显然不是我们期望的,那么该如何解决呢?
回调函数控制异步代码执行顺序
function getData(callback) {
setTimeout(() => {
callback(123);
}, 0);
}
console.log('代码开始执行');
getData(function(n) {
console.log('callback函数被调用了')
console.log(n)
console.log('callback函数结束了')
});
console.log('代码结束执行');
//代码开始执行
//代码结束执行
//callback函数被调用了
//123
//callback函数结束了
我们看到在回调函数中代码是依次进行的
回调函数的作用就是,将异步代码写在一个块级作用域中,当其中的异步函数执行后,才会调用这个回调函数,这时异步函数已经执行完成,所以在回调函数中代码就可以如同步代码一样依次执行
所以问题就解决了,只需要在回调函数中调用其他的异步API就可以保证异步代码的顺序执行
function getData(callback) {
setTimeout(function() {
console.log('2s')
setTimeout(function() {
console.log('0s')
callback(123);
}, 0)
}, 2000)
}
console.log('代码开始执行');
getData(function(n) {
console.log(n)
});
console.log('代码结束执行');
//代码开始执行
//代码结束执行
//2s
//0s
//123