操作与promise"/>
异步操作与promise
同步指的是一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。
异步指的是每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
setTimeout(function cbFn(){console.log('learnInPro');
}, 1000);console.log('sync things');
setTimeout就是一个异步任务
,当JS引擎顺序执行到setTimeout的时候发现他是个异步任务,则会把这个任务挂起,继续执行后面的代码。直到1000ms后,回调函数cbFn才会执行,这就是异步,在执行到setTimeout的时候,JS并不会傻呵呵的等着1000ms执行cbFn回调函数,而是继续执行了后面的代码。
知识剖析
首先js是单线程的语言,即同一时间只能做做一件事。那Js如何实现异步的,异步和单线程不是自相矛盾吗?其实,单线程和异步确实不能同时成为一个语言的特性。js选择了成为单线程的语言,所以它本身不可能是异步的,但js的宿主环境(比如浏览器,Node)是多线程的,宿主环境通过某种方式(事件驱动,下文会讲)使得js具备了异步的属性。
浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:javascript引擎线程,UI渲染线程,浏览器事件触发线程。
1.javascript引擎线程是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。
2.UI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意 UI渲染线程与JS引擎是互斥的,当JS引擎执行时UI线程会被挂起,UI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
3.事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。
常见的异步
- 回调函数
- 事件监听
- 发布/订阅模式(又称观察者模式)
- promise
这里我们重点讲一下promise(一种异步编程的解决方案),
当我们做网络请求时,有时候会有很复杂的数据处理,以至于出现回调地狱式的代码,给后期维护造成了巨大的麻烦,所以我们期望一种更加优雅的方式来处理异步操作,在ES6中,增加了一种属性promise,它就是为此而生的,
普通的异步操作
setTimeout(() => {console.log('异步编程的回调')
},1000)
promise是一个类,所以使用前应该实例化
new Promise((resolve,reject) => {setTimeout(() => {//成功时调用resolve// resolve('see you again')//失败时调用rejectreject('error')},1000)}).then((data) => {console.log(data)}).catch((data) => {console.log(data)})
当然,从表面上看确实是复杂了许多,但其逻辑清晰了不少,同时,传入的参数resolve,reject也是函数,当其调用时,各自有相应的方法响应,前者是then,后者是catch,由于都是函数,所以可以传入相应的参数,所以还可以这样写
new Promise((resolve,reject) => {setTimeout(() => {//成功时调用resolve// resolve('see you again')//失败时调用rejectreject('error')},1000)}).then((data) => {console.log(data)},(data) => {console.log(data)})
//then(参数,参数)
在进行promise操作后,会出现三种状态
- pending 等待状态,请求过程
- fulfill 满足状态,调用函数resolve
- reject 拒绝状态,调用reject函数
链式调用
new Promise((resolve,reject) => {setTimeout(() => {reslove()},1000)}).then(() => {console.log('第一个网络请求')console.log('第一个网络请求')console.log('第一个网络请求')console.log('第一个网络请求')console.log('第一个网络请求')console.log('第一个网络请求')return new Promise((reslove,reject) => {setTimeout(() => {resolve()},1000)})}).then(() => {console.log('第二个网络请求')console.log('第二个网络请求')console.log('第二个网络请求')console.log('第二个网络请求')console.log('第二个网络请求')console.log('第二个网络请求')return new Promise((reslove,reject) =>{setTimeout(() => {resolve()},1000)})}).then(() => {console.log('第三个网络请求')console.log('第三个网络请求')console.log('第三个网络请求')console.log('第三个网络请求')console.log('第三个网络请求')})
这也是链式编程的好处之一
链式其他的写法
<script>/*** 需求:网络请求的数据为aaa,* 第一步:封装为aaa111* 第二步:封装为aaa111222*/new Promise((resolve,reject) => {setTimeout(() => {resolve('aaa')},1000) //模拟的异步操作}).then((data) => {console.log(data,'请求到的数据')// reject是可选参数// 更好的简写属性return Promoise.resolve(data + '111')}).then((data) => {console.log(data,'第一次处理数据')// return new Promise((resolve) => {// resolve(data + '222')// })// 更更更好的简写属性return data + '222'}).then((data) => {console.log(data,'第二次处理')})</script>
仔细阅读上面的代码,reject的简写写法和resolve一样,而且reject可以通过throw抛出异常信息。
最后在补充一点,当遇到同时满足几个请求时,可以使用promise的all方法,此方法的参数要求传入一个可迭代对象
更多推荐
异步操作与promise
发布评论