采用单线程模式工作的原因
JavaScript 采用单线程工作模式,因为JavaScript 最开始是用来在浏览器运行的脚本语言,是用来实现页面的动态交互,实现动态交互的核心是 DOM 操作,所以只能使用单线程模型,否侧会出现复杂的线程同步问题。
单线程
是指,js 执行环境中负责执行代码的线程只有一个
这种模式最大的优势就是更安全,更简单。缺点也很明确,就是如果中间有一个特别耗时的任务,其他的任务就要等待很长的时间,出现假死的情况。
JavaScript 将任务执行模式分成两种,同步模式(Synchronous) ,异步模式(Asynchronous)
异步模式
异步模式的 API 不会等待任务结束才开始下一个任务,对于耗时操作,都是开启过后就立即往后执行下一个任务,耗时任务的后续逻辑一般会通过回调函数的方式定义,耗时任务完成之后会自动执行回调函数
异步模式对于JavaScript语言非常重要,没有它就无法同时处理大量的耗时任务。对于开发者而言。单线程下面的异步最大的难点就是代码执行的顺序混乱。
console.log('global begin')
// 延时器
setTimeout(function timer1 () {
console.log('timer1 invoke')
}, 1800)
// 延时器中又嵌套了一个延时器
setTimeout(function timer2 () {
console.log('timer2 invoke')
setTimeout(function inner () {
console.log('inner invoke')
}, 1000)
}, 1000)
console.log('global end')
// global begin
// global end
// timer2 invoke
// timer1 invoke
// inner invoke
//除了调用栈,还用到了消息队列和事件循环
js线程某个时刻发起了一个异步调用,它紧接着继续执行其他的任务,此时异步线程会单独执行异步任务,执行过后会将回调放到消息队列中,js主线程执行完任务过后会依次执行消息队列中的任务。这里要强调,js是单线程的,浏览器不是单线程的,有一些API是有单独的线程去做的。
回调函数 —— 所有异步编程方案的根基
回调函数:由调用者定义,交给执行者执行的函数 ( 就是把函数作为参数传递,缺点是不利于阅读,执行顺序混乱。)
// callback就是回调函数
// 就是把函数作为参数传递,缺点是不利于阅读,执行顺序混乱。
function foo(callback) {
setTimeout(function(){
callback()
}, 3000)
}
foo(function() {
console.log('这就是一个回调函数')
console.log('调用者定义这个函数,执行者执行这个函数')
console.log('其实就是调用者告诉执行者异步任务结束后应该做什么')
})
还有其他的一些实现异步的方式,例如:事件机制和发布订阅。这些也都是基于回调函数之上的变体。
Promise概述
虽然回调函数是所有异步编程方案的根基。但是如果我们直接使用传统回调方式去完成复杂的异步流程,就会无法避免大量的回调函数嵌套。导致回调地狱的问题。
为了避免这个问题。CommonJS社区提出了Promise的规范,ES6中称为语言规范。
Promise是一个对象,用来表述一个异步任务执行之后是成功还是失败
Promise本质上也是使用回调函数的方式去定义异步任务结束后所需要执行的任务。这里的回调函数是通过 then 方法传递过去的
Promise链式调用
常见误区
- 嵌套使用的方式是使用Promise最常见的误区。要使用promise的链式调用的方法尽可能保证异步任务的扁平化。
链式调用的理解
- promise对象then方法,返回了全新的promise对象。可以再继续调用then方法,如果return的不是promise对象,而是一个值,那么这个值会作为resolve的值传递,如果没有值,默认是undefined
- 后面的then方法就是在为上一个then返回的Promise注册回调
- 前面then方法中回调函数的返回值会作为后面then方法回调的参数
- 如果回调中返回的是Promise,那后面then方法的回调会等待它的结束
Promise异常处理
promise中如果有异常,都会调用reject
方法,还可以使用.catch()
使用.catch
方法更为常见,因为更加符合链式调用
.catch
形式和前面then里面的第二个参数的形式,两者异常捕获的区别:
.catch()
是对上一个.then()
返回的promise进行处理,不过第一个promise的报错也顺延到了catch中,而then的第二个参数形式,只能捕获第一个promise的报错,如果当前then的resolve函数处理中有报错是捕获不到的。
所以.catch是给整个promise链条注册的一个失败回调。推荐使用!!!!