JS异步处理

1、前言

做为一个新人小白来讲,JS的异步处理让我很头疼。。promise

那么什么是异步,为何产生,又怎么处理呢?且看下文浏览器

单线程

做为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操做DOM。这决定了它只能是单线程,不然会带来很复杂的同步问题。好比,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另外一个线程删除了这个节点,这时浏览器应该以哪一个线程为准?
bash

单线程最大的好处就是实现简单,执行环境单纯dom

可是也有一个很致命的缺点:当一个线程须要执行很长时间,那么后续线程就须要一直等着,若是这个线程陷入了死循环,那么后续线程都将得不到执行的机会,页面卡死。异步

因此为了不这个问题,JS制定了两种执行模式,同步模式和异步模式async

异步与同步

同步模式 就是上一段的模式,后一个任务等待前一个任务结束,而后再执行,程序的执行的调入和完成顺序与任务的排列顺序是一致的、同步的;函数

异步模式 则彻底不一样,当一个任务执行时,若它执行时间很短,则它执行完后,后面任务紧跟着它执行,若它涉及异步操做,则将此任务调入主线程队列,异步操做给相应的异步操做理模块执行,与此同时调取下一个任务执行,等到异步模块处理完异步操做后,给主线程一个成功或失败信号,再将异步操做结果调入主线程队列排队,直到执行完。(具体流程参见js事件轮询机制 -_-||)
测试

因此在我理解,异步的关键就是如何实如今异步完成后返回这个信号,来告诉主线程能够进行异步操做结果的处理。ui

2、异步实现方式

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对象用于处理异步操做

它表明一个未完成、可是预计未来会完成的操做

三种状态

  • pending:初始值,不是resolved,也不是rejected
  • resolved:操做成功
  • rejected:操做失败
状态转换
  • 由pending转换为resolved
  • 由pending转换为rejected
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

好吧,仍是安安静静回去看教程吧!!

使用方法

  • async放于function定义以前,await写在async函数中,单独放在外层是不起做用的
  • async/await基于promise实现,被定义的异步函数执行后返回promise(这就能解释为啥多打印出来个promise对象了),若成功,使用.then()来获取结果,不成功呢,用.catch()获取失败结果
  • await等待的后续结果必须返回一个promise,通常的函数调用无论用(。。。好吧,基于promise实现的,证实人家认识promise,愿意等promise,不认识的还不乐意等了,这就是为啥我等延时执行无论用)
因此要想实现上述功能,得让await等的是个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有哪些优点呢?(毕竟新整的东西还不如之前的还不如不整)

  • async/await更加语义化,async 是“异步”的简写,async function 用于申明一个 function 是异步的; await,能够认为是async wait的简写, 用于等待一个异步方法执行完成;
  • async/await是一个用同步思惟解决异步问题的方案(等结果出来以后,代码才会继续往下执行)
  • 能够经过多层 async function 的同步写法代替传统的callback嵌套

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对象的自动转换操做简便

相关文章
相关标签/搜索