做为一个新人小白来讲,JS的异步处理让我很头疼。。promise
那么什么是异步,为何产生,又怎么处理呢?且看下文浏览器
单线程
做为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操做DOM。这决定了它只能是单线程,不然会带来很复杂的同步问题。好比,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另外一个线程删除了这个节点,这时浏览器应该以哪一个线程为准?
bash
单线程最大的好处就是实现简单,执行环境单纯dom
可是也有一个很致命的缺点:当一个线程须要执行很长时间,那么后续线程就须要一直等着,若是这个线程陷入了死循环,那么后续线程都将得不到执行的机会,页面卡死。异步
因此为了不这个问题,JS制定了两种执行模式,同步模式和异步模式async
异步与同步
同步模式 就是上一段的模式,后一个任务等待前一个任务结束,而后再执行,程序的执行的调入和完成顺序与任务的排列顺序是一致的、同步的;函数
异步模式 则彻底不一样,当一个任务执行时,若它执行时间很短,则它执行完后,后面任务紧跟着它执行,若它涉及异步操做,则将此任务调入主线程队列,异步操做给相应的异步操做理模块执行,与此同时调取下一个任务执行,等到异步模块处理完异步操做后,给主线程一个成功或失败信号,再将异步操做结果调入主线程队列排队,直到执行完。(具体流程参见js事件轮询机制 -_-||)
测试
因此在我理解,异步的关键就是如何实如今异步完成后返回这个信号,来告诉主线程能够进行异步操做结果的处理。ui
1. 回调函数this
回调函数 就是将A函数做为B函数的参数传递给B,等B函数执行完成后再执行
好比我想实现B函数实现一系列复杂运算(或异步操做),实现完成后改变dom,而A函数须要在B函数更新完dom后,再对dom执行一些操做
用回调实现:
var domList = [];
function B(dom,callback){
setTimeout(()=>{
dom.push(1);
callback();
},2000);
}
function A(){
console.log(domList);
}
B(domList,A)复制代码
这里用延迟执行来代替异步操做,上面结果在两秒后打印 [1],在B中的callback调用这里,就是手动模拟了异步操做完成的信号,告诉A能够进行后续操做了。
注意:处理异步能够用回调,可是回调不必定就是异步
2. 事件监听
采起事件驱动模式,即定义一个事件,当这个事件发生时,执行一些处理。
在异步中的实现即 先定义事件发生时的处理函数而且监听这个事件,当进入异步且执行完后触发事件,进而触发监听这个事件相关联的处理函数。
监听的函数有:on、bind、listen、addEventaddEventListener、observe
仍是上述功能,用事件监听实现:
var domList = [];
function A(){
console.log(domList);
}
// 绑定监听 当B发生updateComplate事件时,执行A函数
B.on('updateComplate',A);
function B(){
setTimeout(()=>{
domList.push(1);
B.trigger('updateComplate');
},2000);
}
A();复制代码
这种方式容易理解,也能够为一个事件监听绑定多个事件处理函数
可是程序会变成事件驱动型的,运行流程不清晰
3. 发布/订阅
发布者发出通知 =>主题对象收到通知并推送给订阅者 => 订阅者执行相应的操做。
// 一个发布者 publisher,功能就是负责发布消息 - publish
var pub = {
publish: function () {
dep.notify();
}
}
// 多个订阅者 subscribers, 在发布者发布消息以后执行函数
var sub1 = {
update: function () {
console.log(1);
}
}
var sub2 = {
update: function () {
console.log(2);
}
}
var sub3 = {
update: function () {
console.log(3);
}
}
// 一个主题对象
function Dep() {
this.subs = [sub1, sub2, sub3];
}
Dep.prototype.notify = function () {
this.subs.forEach(function (sub) {
sub.update();
});
}
// 发布者发布消息, 主题对象执行notify方法,进而触发订阅者执行Update方法
var dep = new Dep();
pub.publish();复制代码
4. promise对象
promise对象用于处理异步操做
它表明一个未完成、可是预计未来会完成的操做
三种状态:
对上述功能的实现:
var domList = [];
function B(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
domList.push(1);
resolve(domList);
},2000);
});
}
function A(){
console.log(domList);
}
B().then((result)=>{
A();
}).catch(err=>{
console.log(err)
});复制代码
promise中,resolve和reject至关于异步操做完成的触发信号,then至关于resolve后的成功处理函数,catch至关于reject后的失败处理函数
promise的then能够链式调用
var domList = [];
function B(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
domList.push(1);
resolve(domList);
},2000);
});
}
function A(){
console.log(domList);
domList.push(2);
}
B().then((result)=>{
A();
}).then((result)=>{
A();
}).catch(err=>{
console.log(err)
});复制代码
5. async/await
初识async/await是在实际代码中,是怎么写的呢,async定义一个函数,在里面 使用await 可让后面的代码等待此行执行完后再执行,我去,这我一理解,那我须要耗时的函数定义为async ,实际耗时操做加一个await,那不是分分钟变异步为同步了吗(-_-'')
因而年轻的我开始写测试了
async function test(){
console.log(1);
await setTimeout(()=>{
console.log(2);
},2000);
console.log(3);
}
test();复制代码
test执行应该是 1 ,两秒后 2,3 了呗,结果呢。。。。 1,3,两秒后 2,控制台还多了个什么Promise
好吧,仍是安安静静回去看教程吧!!
使用方法:
function asyncF(){
return new Promise((resove,reject)=>{
setTimeout(()=>{
resove('2');
},2000);
})
}
async function callAsync(){
console.log('1')
await asyncF().then(result=>{
console.log(result);
});
console.log('3');
}
callAsync();复制代码
这样就能够执行了
async/await基于promise实现的,那他比promise有哪些优点呢?(毕竟新整的东西还不如之前的还不如不整)
function asyncF(x) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2*x);
}, 2000);
})
}
async function callAsync() {
let r1 = await asyncF(1);
let r2 = await asyncF(r1);
let r3 = await asyncF(r2);
console.log(r3)
}
callAsync();复制代码
相似同步的异步使用,以及async函数对promise对象的自动转换操做简便