认识 Promise
Promise 是在 ES6 中出现的语法,主要用于处理 JavaScript 中的异步执行流程,使其能够像同步代码一样处理。在深入了解 Promise 之前,我们先来了解一下什么是异步。
异步
异步处理方式会在不等待每一行代码执行完成的状态下继续执行下一行代码。例如:
javascript
let str = 'init';
const func = () => {
str = 'changed';
};
setTimeout(func, 3000);
console.log(str); // 输出 'init'
上述代码段的执行顺序如下:
- 声明字符串
str
并赋值为init
。 - 声明方法
func
,作用是将字符串str
的值改为changed
。 - 使用
setTimeout
在三秒后执行func
更改str
的值。 - 最后在控制台中输出字符串
str
的值。
关键在于第 3 步和第 4 步。在输出 console.log
时,程序并不会等待 setTimeout
执行完成,而是继续向下执行,直到三秒后才会回头执行 func
更改 str
的值。
虽然使用异步可以让执行速度不会因为某一行代码而卡住整个程序,但有时候我们仍然需要先通过 AJAX 获取数据,再继续执行处理数据的代码。
到这里是不是有点熟悉?在使用 JQuery
的 ajax
送出请求时就拥有了 async
这个属性来处理这个状况,当设置为 false
代表同步,这时就得等 ajax
等到响应完成后,才会继续执行下方的代码,就像下方的例子:
javascript
let data = {}
//使用 async 将 ajax 请求改为同步。
$.ajax({
url: '',
async: false,
success: (status)=>{
data = status
}
)}
//data为statue
console.log(data)
但因现今前端工程在网页中的份量越来越重的关系,需要处理同步处理的地方也越来越多, Promise
就这样诞生了。
Promise 的基本用法
如上所述,Promise 是用来处理异步操作的部分。在说明用法前,我们先通过图片简单了解一下原理:
上图为 Promise
的生命周期,每个 Promise
都会经过 pending
状态,在 pending
后分别会有成功时走向的的 fulfill
,及失败时的 reject
,并通过 .then()
在成功时接着处理数据,或是用 .catch()
对失败的情况进行处理,当然!不论是哪一条路,也可以再 return
一个新的 Promise
延续处理。
现在,我们来实现一个基本的 Promise:
javascript
const newPromise = new Promise((resolve, reject)=>{
/**成功时返回**/
resolve(status)
/**失败时返回**/
reject(status)
}).then((data)=>{
/**以 then 接续成功时的处理**/
}).catch((error)=>{
/**以 catch 接续失败时的处理**/
})
一个基本的 Promise
会带有两个函数作为参数,这两个函数代表了生命周期内的 fulfill
及 reject
,不论是成功时返回的 resolve
和失败时返回的 reject
都能传入参数, 使对应的 .then
和 .catch
可以接收 Promise
内得到的数据,做出不同的处理,再稍微了解用法后,便可把第一个例子改为 Promise
版本:
javascript
const newPromise = new Promise((resolve, reject)=>{
setTimeout(()=>{resolve('changed')}, 3000)
}).then((data)=>{
console.log(data) //3 秒后执行,并打印出'changed'
})
不同于第一个例子,通过 Promise
来处理获取数据,并把得到的数据通过 resolve
传到 .then
中处理,而 .then
内的代码也不会偷跑,会确实等到 Promise
内调用 resolve
结束后才会接着执行。
延续上方的例子,把 reject
对应的 catch
也加进去:
javascript
const newPromise = new Promise((resolve, reject)=>{
setTimeout(()=>{
if(Math.random() > 0.5){
resolve('changed')
}
else{
reject('error')
}
}, 3000)
}).then((data)=>{
console.log(data)
}).catch((error)=>{
console.log(error)
})
上方的在 setTimeout
内加上了 Math.random()
取随机数(会返回 0~1 之间的值),并借由判断该随机数是否大于 0.5 来模拟两种情况,当大于时代表成功,小于时代表失败,当然! .catch
也和 .then
相同,在没有 Promise
内的 reject
调用下是不会执行的。
Promise 进阶用法
了解过基本用法,我们知道 .then
能够在 Promise 中以 resolve
做同步的执行调用,但 .then
本身也可以拥有返回值,并由下一个 .then
接收继续执行:
javascript
const newPromise = new Promise((resolve, reject)=>{
setTimeout(()=>{resolve('changed')}, 3000)
}).then((data)=>{
console.log(data) // 'changed'
return 'last changed'
}).then((data)=>{
console.log(data) // 'last changed'
})
第二个 .then
会接收上一个 .then
的返回值,并将它打印出,但是需要注意的是,这种多个 .then
的用法,并不适用于同步执行,也就是说只要脱离了 Promise
就会回到异步,例如以下例子:
javascript
const newPromise = new Promise((resolve, reject)=>{
setTimeout(()=>{resolve('changed')}, 3000)
}).then((data)=>{
console.log(data)
setTimeout(()=>{data = 'last changed'},2000)
return data
}).then((data)=>{
console.log(data) // 'changed'
})
上方例子在第一个 .then
中使用 setTimeout
在 2 秒后改变 data
的值并将他返回,但执行后会发现,在第二个 .then
中出现在 console
上的还是旧值,并不会等到 setTimeout
执行完后才接着下一个 .then
。
另外返回值也可以是另一个 Promise
:
javascript
const lastPromise = new Promise((resolve, reject)=>{
setTimeout(()=>{resolve('last changed')},2000)
})
const newPromise = new Promise((resolve, reject)=>{
setTimeout(()=>{resolve('changed')}, 3000)
}).then((data)=>{
console.log(data)
return lastPromise.then((lastData)=>{return lastData})
}).then((data)=>{
console.log(data) // 'last changed'
})
在第一个 .then
的返回了 lastPromise
的 .then
返回的内容,也就是 last changed
,因此最后一个 .then
接收到的 data
也会是 last changed
。
上方例子也有需要注意的地方,那就是每个 Promise
都会有自己的生命周期,所以在 lastPromise
被建构出来的时候,就会开始进行 pending
,不会等到 newPromise
的第一个 .then
要求返回数据时才开始执行 setTimeout
的 2 秒,因此上方程序的总执行时间并不会是两个 setTimeout
加起来的时间 5 秒,而是 3 秒,因为在 Promise
外,都是异步的,所以两个 Promise
在建构后的 pending
是异步的。
如果需要将两个 Promise
的执行也变成同步,那只需将上方的 lastPromise
置于 newPromise
的 .then
中就行了,如此一来就会变成 3 秒加上 2 秒的同步执行了:
javascript
const newPromise = new Promise((resolve, reject)=>{
setTimeout(()=>{resolve('changed')}, 3000)
}).then((data)=>{
console.log(data)
//第二个Promise
return new Promise((resolve, reject)=>{
setTimeout(()=>{resolve('last ' + data)},2000)
}).then((lastData)=>{
return lastData
})
}).then((data)=>{
console.log(data) // 'last changed'
})
如此一来只有在进入 newPromise
的 .then
时,才会去新建构另一个 Promise
并利用他的 .then
返回了一个新值,等到新建构的 Promise
生命周期结束后,才会执行 newPromise
最后一个 .then
,不过这种方式会累加执行的时间,即总共需要 3 秒 + 2 秒 = 5 秒。
以上就是本篇对 Promise 的介绍,希望对你有所帮助。如果文章中有任何问题或是不理解的地方,欢迎留言交流!