这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战
JavaScript
的执行环境如浏览器或者nodejs
都是以单线程作为主线程来运行的。单线程,就是指JS引擎中负责解释和执行JavaScript
代码的线程只有一个,意思就是js
的执行上下文一次只能完成一项任务,这个任务执行完后才能执行下一个,它会阻塞其他任务,这就是主线程。
同步
同步运行,同步任务是存储在栈上的,每次会同步清楚每个同步任务,一次只能运行一个任务,函数调用后需等到函数执行结束,返回执行的结果,才能进行下一个任务,这样就会导致线程阻塞。
异步
异步模式,即与同步模式相反,异步任务是以队列的形式来储存的,可以一起执行多个任务,函数调用后不会立即返回执行的结果,如果前一个人物需要等待,可先执行后面的任务,等到前置任务结果返回后再继续回调 例:
setTimeout(function() {
console.log('1');
}, 0);
setTimeout(function() {
console.log('3');
}, 0);
console.log('2');
复制代码
2
1
3
复制代码
上面的代码输出顺序为2,1,3。即在事件循环中首先会录入所有的同步任务依次执行,执行完所有的同步任务之后才会执行异步任务。
回调函数
回调函数被作为实参传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数,称为回调函数。
function greeting(name) {
alert('Hello ' + name);
}
function processUserInput(callback) {
var name = prompt('name');
callback(name);
}
processUserInput(greeting);
复制代码
然而需要注意的是,回调函数经常被用于继续执行一个异步 完成后的操作,它们被称为异步回调。
同步回调依然会阻塞代码的执行,而异步回调如setTimeout
的回调函数就会等待同步任务执行完后才会执行。
由于通常的业务中会使用到多个异步回调进行数据获取或者处理。这样为了等待异步任务的执行完毕,我们需要在异步任务的回调函数中进行下一步操作,如果要处理多个异步回调事件,就会形成一个个回调函数的嵌套,代码一多,就很不方便阅读与开发。如:
function load() {
$.ajax({
url: 'xxx.com',
data: '123',
success: function(res) {
init(res, function(res) {
render(res, function(res) {
//...
});
});
}
}
}
load();
复制代码
于是就有Generator
的出现以及ES6Promise
的出现,来解决这种问题,Generator
在其他语言中早就有了,它的作用是暂停函数的执行,通过yield
来控制函数的执行,从而实现异步管理功能,但是由于写法比较复杂,一直没有得到广泛使用。直到Promise
的出现,首次提出了Pending
,fullfilled
,rejected
三种状态来管理函数的执行,更方便的管理我们的代码。在之后的es7
中根据promise的使用,又提出了async
以及await
的语法糖,实际是Generator
的语法糖,通过它们我们可以以同步的书写方式,来得到异步代码调用的效果。具体原理由下一篇文章分析。