JS中的异步解决方案

JS中异步处理方案

本章介绍三种异步处理方案:ios

  • 回调函数(callback)
  • promise
  • async/await

若是想要深刻了解 promisegenerator+coasync/await 的话,建议参考个人这篇文章《深刻理解 promise、generator+co、async/await 用法》git

回调函数(callback)

回调函数应该属于最简单粗暴的一种方式,主要表现为在异步函数中将一个函数进行参数传入,当异步执行完成以后执行该函数编程

话很少说,上代码:axios

// 有三个任务console.log(1)console.log(2)console.log(3)
// 过5s执行任务1,任务1执行完后,再过5s执行任务2.....
window.setTimeout(function(){
	console.log(1)
	window.setTimeout(function(){
		console.log(2)
		window.setTimeout(function(){
			console.log(3)
		},5000)
	},5000)
},5000)
复制代码

看出这种方式的缺点了吗?没错,试想,若是再多几个异步函数,代码总体的维护性,可读性都变的极差,若是出了bug,修复的排查过程也变的极为困难,这个即是所谓的 回调函数地狱promise

promise

promise简单的说就是一个容器,里面保存着某个将来才会结束的时间(一般是一个异步操做)的结果。从语法上说,promise就是一个对象,从它能够获取异步操做的消息。promise提供统一的API,各类异步操做均可以用一样的方法处理。bash

如何理解:异步

  • 没有异步就不须要promise
  • promise自己不是异步,只是咱们去编写异步代码的一种方式

promise有所谓的 4 3 2 1async

4大术语
必定要结合异步操做来理解
既然是异步,这个操做须要有个等待的过程,从操做开始,到获取结果,有一个过程的异步编程

  • 解决(fulfill)指一个 promise 成功时进行的一系列操做,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 promise 实现多以 resolve 来指代之
  • 拒绝(reject)指一个 promise 失败时进行的一系列操做
  • 终值(eventual value)所谓终值,指的是 promise 被解决时传递给解决回调的值,因为 promise 有一次性的特征,所以当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)
  • 据因(reason)也就是拒绝缘由,指在 promise 被拒绝时传递给拒绝回调的值

3种状态
在异步操做中,当操做发出时,须要处于等待状态
当操做完成时,就有相应的结果,结果有两种:函数

  • 成功了
  • 失败了

一共是3种状态,以下:

  • 等待态(Pending (也叫进行态)
  • 执行态(Fulfilled)(也叫成功态)
  • 拒绝态(Rejected) (也叫失败态)

图片加载失败

针对每一种状态,有一些规范:

等待态(Pending)
处于等待态时,promise 需知足如下条件:

  • 能够迁移至执行态或拒绝态

执行态(Fulfilled)
处于执行态时,promise 需知足如下条件:

  • 不能迁移至其余任何状态
  • 必须拥有一个不可变的终值

拒绝态(Rejected)
处于拒绝态时,promise 需知足如下条件:

  • 不能迁移至其余任何状态
  • 必须拥有一个不可变的据因

2种事件
针对3种状态,只有以下两种转换方向:

  • pending –> fulfilled
  • pendeing –> rejected

在状态转换的时候,就会触发事件:

  • 若是是pending –> fulfiied,就会触发onFulFilled事件
  • 若是是pendeing –> rejected,就会触发onRejected事件

在调用resolve方法或者reject方法的时候,就必定会触发事件

须要注册onFulFilled事件 和 onRejected事件
针对事件的注册,Promise对象提供了then方法,以下:
promise.then(onFulFilled,onRejected)

针对 onFulFilled,会自动提供一个参数,做为终值(value)
针对 onRejected,会自动提供一个参数,做为据因(reason)

1个对象
promise

注:只有异步操做的结果,能够决定当前是哪种状态,任何其余的操做都没法改变这个状态

简单来说,就仍是promise中有着三种状态pending,fulfilled,rejected。在代码中咱们能够控制状态的变动

new Promise(function(resolve,reject){
	console.log("pending");
	console.log("pending");
	resolve();
	reject();
})
复制代码

建立一个Promise对象须要传入一个函数,函数的参数是resolve和reject,在函数内部调用时,就分别表明状态由pending=>fulfilled(成功),pending=>rejected(失败)

一旦promise状态发生变化以后,以后状态就不会再变了。好比:调用resolve以后,状态就变为fulfilled,以后再调用reject,状态也不会变化

在建立promise对象,只须要根据需求,转换状态便可。无非就是调用两个函数:

  • resolve,传递value
  • reject,传递reason

Promise对象在建立以后会马上执行,所以通常的作法是使用一个函数进行包装,而后return一个promise对象

function betray(){
	return new Promise(function(resolve,reject){
		...//异步操做
	})
}
复制代码

在使用时能够经过promise对象的内置方法then进行调用,then有两个函数参数,分别表示promise对象中调用resolve和reject时执行的函数

function betray(){
	return new Promise(function(resolve,reject){
		setTimeout(function(){
			resolve();
		},1000)
	})
}


betray().then(function(){
	...//对应resolve时执行的逻辑
},function(){
	...//对应reject时执行的逻辑
})
复制代码

也能够用 catch 来执行失败态

catch方法,用于注册 onRejected 回调

在这里要明白两件事情:

  • catch实际上是then的简写,then(null,callback)
  • then方法调用以后,仍然返回的是promise对象,因此能够链式调用

使用以下:

betary().then(res=>...//对应resolve时执行的逻辑).catch(err=>...//对应reject时执行的逻辑)
复制代码

可使用多个then来实现链式调用,then的函数参数中会默认返回promise对象

betray().then(function(){
	...//对应resolve时执行的逻辑
},function(){
	...//对应reject时执行的逻辑
})
.then(function(){
	...//上一个then返回的promise对象对应resolve状态时执行的逻辑
},function(){
	...//上一个then返回的promise对象对应reject状态时执行的逻辑
})
复制代码

使用promise来解决回调地狱的作法就是使用then的链式调用

function fnA(){
	return new Promise(resolve=>{
		...//异步操做中resolve
	})
}
function fnB(){
	return new Promise(resolve=>{
		...//异步操做中resolve
	})
}
function fnC(){
	return new Promise(resolve=>{
		...//异步操做中resolve
	})
}

fnA()
.then(()=>{
	return fnB()
})
.then(()=>{
	return fnC()
})
复制代码

特色是:

  • then方法一般是表示异步操做成功时的回调,也能够用catch方法表示异步操做失败时的回调
  • 在调用的时候then在先后,catch在后
  • then方法能够调用屡次,前一个then的返回值,会做为后一个then的参数
  • 支持链式调用

async/await

async、await是什么?

async顾名思义是“异步”的意思,async用于声明一个函数是异步的。而await从字面意思上是“等待”的意思,就是用于等待异步完成。而且await只能在async函数中使用

一般async、await都是跟随Promise一块儿使用的。为何这么说呢?由于async返回的都是一个Promise对象同时async适用于任何类型的函数上。这样await获得的就是一个Promise对象(若是不是Promise对象的话那async返回的是什么 就是什么);

await获得Promise对象以后就等待Promise接下来的resolve或者reject。

async、await解决了什么?

传统的回调地狱式写法:

getData(a=>{
	getMoreData(a,b=>{
		getMoreData(b,c=>{
			console.log(c)
		});
	});
});
//不行了,再多写要迷了
复制代码

Promise改进后的写法:

getData()
.then(a=>getMoreData(a))
.then(b=>getMoreData(b))
.then(c=>getMoreData(c))
复制代码

async/await改进后:

(async()=>{
	const a = await getData;
	const b = await.getMoreData(a);
	const c = await.getMoreData(b);
	const d = await.getMoreData(c);
})();
复制代码

async、await写法

先来看看同步写法:

console.log(1);

setTimeout(function () {
  console.log(2);
}, 1000);

console.log(3);
复制代码

输出结果:

1
3
2
复制代码

能够看到输出的顺序并非咱们代码中所写的那样,下面来看下async、await是如何解决这个问题的

(async function () {

  console.log(1);

  await new Promise(function (resolve, reject) { 
    setTimeout(function () {
      console.log(2);
      resolve();
    }, 1000);
  });

  console.log(3);

}())
复制代码

输出结果:

1
2
3
复制代码

能够看到这种写法的输出已经符合了咱们的预期

async 的定义:

  • async函数会返回一个Promise对象
  • 若是async函数中是return一个值,这个值就是Promise对象中resolve的值
  • 若是async函数中是throw一个值,这个值就是Promise对象中reject的值

await 的定义:

  • await只能在async里面
  • await后面要跟一个promise对象

常规的promise对象会被js先暂存到eventloop(事件队列)中,由于js是单线程执行的,等执行栈空了以后,才会将事件队列中的事件取出放入执行栈中执行

上述代码中先是将整段代码改形成了一个async(async能够用于任何函数)函数,而后又将setTimeOut改形成了一个Promise对象


使用第三方Promise库

下面简单介绍一下第三方的Promise库

对开发中使用promise进行小结:

  • 没有异步,就不须要promise
  • 不使用promise,其实也是能够解决异步编程的问题。使用promise,会使异步的编码变得更加优雅,功能会更强
  • 在进行promise编程的使用,有以下两个场景:
    • 直接使用别人封装好的promise对象,好比fetch、axios
    • 须要本身封装promise对象

注意:axios和fetch必须使用promise方式,如:

图片加载失败!

针对本身封装promise对象,又能够有以下两种方式:

  • 本身封装
  • 可使用第三方的promise库

好比,针对第三方的promise库,有两个知名的库:

  • bluebird
  • q.js

能够利用bluebird 和 q.js 快速的生成promise对象。

以bluebird为例,在服务端演示其用法。

第一步:安装

图片加载失败!

第二步:使用

图片加载失败!

是否是以为 so-easy

以上就是解决JS异步的三种方法,还有好多不足之处,但愿能够继续学习和深刻理解


^_<

相关文章
相关标签/搜索