同步模式
同步模式指的就是我们代码中的任务依次执行,程序执行的顺序与代码的编写顺序一致。
以下代码为同步模式的代码,具体分析其执行顺序
// 首先分析代码结构,本段代码为同步模式,js在读取到代码时,先将一个(anonymous)匿名函数放到
// 调用栈。
console.log('Global begin')
// 在读取到第一行console.log(‘Global begin’)时,将其压到调用栈,随后去执行,当控制台打印出
//结 果后,将其弹出调用栈,继续下一行代码;
// 在读取到bar函数及foo函数时,由于其并未执行,因此调用栈内无执行任务;
function bar() {
console.log('Bar task')
}
function foo() {
console.log('Foo task')
bar()
}
// 在读取到foo()函数调用时,首先将foo()压入调用栈,遇console.log(‘Foo task’)代码,将其压入
// 调用栈,执行完毕后弹出调用栈。随后将bar()压入调用栈,程序去bar()函数内部解析,
// 将console.log(‘Bar task’)压入调用栈。foo()函数执行完毕后,依次将bar()、foo()函数弹出调
// 用栈;
foo()
// 最后将console.log(‘Global end’)压入调用栈并执行,随后将其弹出,代码运行完成,
// 将(anonymous)弹出调用栈,程序完全结束。
console.log('Global end')
输出结果:
Global begin
Foo task
Bar task
Global end
异步模式
在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操作。在服务器端,"异步模式"甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有http请求,服务器性能会急剧下降,很快就会失去响应。
异步调用并不会阻止代码的顺序执行,而是在将来的某一个时刻触发设置好的逻辑,所以我们
- 并不知道逻辑什么时候会被调用
- 只能定义当触发的时候逻辑是什么
- 只能等待,同时可以去处理其他的逻辑
// 本段代码为异步模式,js在读取到代码时,先将一个(anonymous)匿名函数放到调用栈。
// 将第一行console.log(‘global begin’)压入调用栈并执行后弹出,此时控制台打印global begin;
console.log('global begin')
// 程序到setTimeout时,首先将setTimeout(timer1)压入调用栈,在web API线程放入timer1计时器,
// 倒计时1.8s,随后将setTimeout(timer1)弹出调用栈;
setTimeout(function timer1() {
console.log('timer1 invoke')
}, 1800)
// 将setTimeout(timer2)压入调用栈,web API线程放入timer2计时器,倒计时1s,
// 随后将setTimeout(timer2)弹出调用栈;
setTimeout(function timer2() {
console.log('timer2 invoke')
setTimeout(function inner() {
console.log('inner invoke')
}, 1000)
}, 1000)
// 随后将console.log(‘global end’)压入调用栈并执行后弹出调用栈,代码执行完毕,
// 将anonymous弹出调用栈;
console.log('global end')
// web API将timer1与timer2依次放入事件队列,此时timer2优先倒计时完毕,进入调用栈,
// 然后执行内部代码。将console.log(‘timer2 invoke’)压入调用栈并执行后弹出。
// 随后遇setTimeout(inner),将其压入调用栈并在Web API加入inner计时器,倒计时1s。
// setTimeout(inner)弹出调用栈。
// 此时随倒计时,timer1倒计时完毕,程序进入timer1内部,将console.log(‘timer1 invoke’)压入调用栈并执行后弹出。
// 随后inner()计时器进入任务队列,在倒计时结束后,压入调用栈并执行后弹出。至此,程序执行完毕。
输出结果:
global begin
global end
timer2 invoke
timer1 invoke
inner invoke
回调函数
- 所有异步编程方案的根基
- 由调用者定义,交给执行者执行的函数
- 回调函数指的是需要在将来不确定的某一时刻异步调用的函数。通常,在这种回调函数中,我们经常需要频繁地访问外部数据。
function foo(callback) {
setTimeout(function () {
callback()
}, 3000)
}
foo(function () {
console.log('这是一个回掉函数')
console.log('调用者定义这个函数,执行者执行这个函数')
console.log('起始就是调用者告诉执行者:异步任务结束后应该做什么')
})