异步编程、Promise

55 阅读4分钟

异步代码的问题在于,不知道何时代码执行完毕。比如

let x = 3;
setTimeout(() => x = x + 4 , 1000)

由于下面的setTimeout是异步执行的,我们不能准确知道x值在什么时候改变了。

回调函数

ajax ({
  url: 'url',
  sucess :function () {
    
  }
})

回调函数是一种解决方案。

回调函数在异步请求结束时,会回调一个函数,来通知异步请求的结束。

但是,回调函数有一个问题。如果需要多次进行异步请求,就会这样:

ajax ({
	url: 'url1',
	sucess :function () {
		ajax ({
			url: 'url2',
			sucess :function () {
				ajax ({
					url: 'url3',
					sucess :function () {

					}
				})
			}
		})
	}
})

会变成嵌套的回调地狱,这样可读性差,复用性也差。

ES6对于处理异步代码提出了Promise的解决方式。

Promise

Promise是一种异步代码解决机制。

Promise是一种状态机

Promise有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)

这三种状态抽象地表示了这段异步代码的状态。

  • pending表示尚未开始,或者正在执行中
  • fulfilled表示已经成功
  • rejected表示没有成功

创建Promise时,Promise处于pending状态。状态的改变有两种情况:

  • Promise执行成功,由pending状态变为fulfilled状态
  • Promise执行失败,由pending状态变为rejected状态

见上图,状态机只有一个灯,所以只能处于一种状态。并且状态一旦改变,就不能再改变了。

创建promise实例时,需要传入一个执行器函数,这个执行器函数接收两个函数作为参数,并且这个执行器函数会立即执行。

const promise = new Promise((resolve, reject) => {
	// 异步请求处理
	if(/异步请求标识符/) {
		resolve()
	} else {
		reject()
	}
		
})

并且,执行器函数是同步执行的。promise.then()才是异步执行的。

Promise函数的结构

const promise = new Promise((resolve, reject) => {
	// 异步请求处理
	if(/异步请求标识符/) {
		resolve()
	} else {
		reject()
	}
		
})

Promise的值传递

如果我们只需要判断之后,得知异步请求是成功还是失败,那么我们只需要执行上述的“状态机”就可以了。比如网络请求,如果状态码在200-299之间,异步请求成功,状态机状态变为完成;如果状态码不在200-299之间,异步请求失败,状态机状态变为失败。

但有时,我们在异步处理完成后是需要传值的,只告知状态变化是不够的。

Promise的基本流程

在执行器函数中,判断异步请求的结果。如果结果为true,异步请求成功,则调用resolve()函数;如果结果为false,异步请求失败,则调用rejected()函数。

对于成功或者是失败的异步请求进行处理,就需要用到then()函数和catch()函数。

resolve()是then()的数据源,then中接收的参数,是由resolve传递进来的,这个由resolve()传递给then()的参数叫做解决值

只要promise中出现了异常,就会自动调用reject,改变状态,这个由reject传递给catch()的参数叫做拒绝理由。出现异常会自动被catch捕获,不需要我们去特意调用一次reject。

Promise传值过程

resolve()传递的值,是then()的参数

const promise = new Promise((resolve, reject) => {
	resolve(1)
})

promise.then((result) => {
	console.log(result) //1
})

上一个then()中return的值,是下一个then()的参数

const promise = new Promise((resolve, reject) => {
	resolve(1)
})

promise.then((result) => {
	console.log(result) //1
	return 2
}).then((result) => {
	console.log(result) //2
})

链式调用

调用then函数时,回调函数中会接受一个函数,并且调用后,会返回一个新的Promise的实例,因此可以链式调用。上一轮then()调用的return值,会当做下一轮then()函数接收的参数值。

catch()是调用了rejected(),即执行失败后的回调函数。

Promise.all()和Promise.race()

Promise.all()

all:意为“全部”。它的状态由所有promise决定。

const p = Promise.all([p1, p2, p3])

所有的promise都fulfilled才会fulfilled,有一个失败了,第一个失败reject的返回值就会作为catch()的回调函数的参数。

Promise.race()

race:意为“竞争”。主要看最先是谁变化,它的状态会跟随最先变化的走。

const p = Promise.race([p1, p2, p3])

race()其中任意一个promise,一旦成功或者失败,Promise.all()的状态就按照它来走。