前言
在处理异步操作时会使用回调函数,使用的回调函数越多,回调函数互相嵌套则会形成回调地狱,此时代码结构会变得丑陋并且越来越难以维护。
setTimeout(function() {
console.log('第一层');
setTimeout(function() {
console.log('第二层');
setTimeout(function() {
console.log('第三层');
setTimeout(function() {
console.log('第四层');
}, 1000)
}, 1000)
}, 1000)
}, 1000)
我们可以使用Promise来解决这个问题
Promise
Promise
对象用于表示一个异步操作的最终完成(或失败)及其结果值。
描述
一个 Promise 对象代表一个在这个 promise 被创建出来时不一定已知值的代理。它让你能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise,以便在未来某个时候把值交给使用者。
一个 Promise 必然处于以下几种状态之一:
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(fulfilled):意味着操作成功完成。
- 已拒绝(rejected):意味着操作失败。
构造函数
Promise()
:创建一个新的 Promise 对象。该构造函数主要用于包装还没有添加 promise 支持的函数。
创建一个Promise实例对象:
const done = true
const promise = new Promise((resolve, reject) => {
if (done) {
const str = '已兑现'
resolve(str)
} else {
const str = '已拒绝'
reject(str)
}
})
console.log(promise)
done为true,输出以下内容:
done为false,输出以下内容:
此时返回的是Promise对象,内容是无法直接使用的,可以通过实例方法
来获取内容。
实例方法
Promise.prototype.then()
:为 promise 添加一个被兑现状态的回调函数
Promise.prototype.catch()
:为 promise 添加一个被拒绝状态的回调函数
Promise.prototype.finally()
:为 promise 添加一个被兑现和被拒绝状态的回调函数,在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数。
示例:
const done = true
const promise = new Promise((resolve, reject) => {
if (done) {
const str = '已兑现'
resolve(str)
} else {
const str = '已拒绝'
reject(str)
}
})
promise.then(res => {
// 返回状态为 resolved
console.log(res);
}).catch(err => {
// 返回状态为 rejected
console.log(err);
}).finally(() => {
// 返回状态为 (resolved 或 rejected)
console.log('finally');
})
如上所示,done为true则触发then()
方法输出'已兑现'
,done为false则触发catch()
方法捕获错误输出'已拒绝'
,无论哪种最终都会触发finally()
方法。
一个更常见的示例是一种被称为 Promisifying 的技术,这项技术能够使用经典的 JavaScript 函数来接受回调并使其返回 promise:
// nodejs示例
const fs = require('fs')
const getFile = (fileName) => {
return new Promise((resolve, reject) => {
fs.readFile(fileName, (err, data) => {
if (err) {
reject(err) // 调用 `reject` 会导致 promise 失败,无论是否传入错误作为参数,
return // 且不再进行下去。
}
resolve(data)
})
})
}
getFile('/etc/passwd')
.then(data => console.log(data))
.catch(err => console.error(err))
// axios请求示例,axios返回的就是Promise对象
import axios from 'axios'
const function getList() {
return axios({
url: 'http://xxx/api/getList',
method: 'get'
})
}
getList()
.then(res => console.log(res))
.catch(err => console.error(err))
Promise 在 ES2015
中进行了标准化和引入,而在 ES2017 中添加了 async/await
,上面的示例可以再做优化
// api.js
import axios from 'axios'
export function getList() {
return axios({
url: 'http://xxx/api/getList',
method: 'get'
})
}
// App.vue
import {
getList } from './api.js'
created() {
this.init()
}
methods: {
async init() {
// await 是一个修饰符,只能放在async定义的函数内
// 可以获取Promise中返回的内容(resolve或reject的参数)
// 且取到值后语句才会往下执行
const res = await getList()
console.log(res)
}
}
静态方法
Promise.resolve(value)
:返回一个状态由给定 value 决定的 Promise 对象。如果value是个 Promise 对象,状态就会根据value的最终状态来决定;否则,返回的 Promise 对象状态为已兑现,并且将该 value 传递给对应的 then 方法。
Promise.reject(reason)
:返回一个状态为已拒绝的 Promise 对象,并将给定的失败信息传递给对应的处理函数。
示例:
const status = response => {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
}
return Promise.reject(new Error(response.statusText))
}
fetch('/todos.json')
.then(status) // 注意,`status` 函数实际上在这里被调用,并且同样返回 promise,
.then(data => {
console.log('res:', data)
})
.catch(error => {
console.log('请求失败', error)
})
Promise.all(iterable)
:等到所有的 promise 对象都成功或有任意一个 promise 失败,需要同步多个不同的Promise时使用
示例:
const f1 = fetch('/something.json')
const f2 = fetch('/something2.json')
Promise.all([f1, f2])
.then(res => {
console.log('结果的数组', res)
})
.catch(err => {
console.error(err)
})
ES2015 解构赋值语法也可以执行:
Promise.all([f1, f2]).then(([res1, res2]) => {
console.log('结果', res1, res2)
})
Promise.race(iterable)
:等到任意一个 promise 的状态变为已敲定(已兑现或已拒绝)
示例:
const first = new Promise((resolve, reject) => {
setTimeout(resolve, 500, '第一个')
})
const second = new Promise((resolve, reject) => {
setTimeout(resolve, 100, '第二个')
})
Promise.race([first, second]).then(result => {
console.log(result) // 第二个
})
Promise.allSettled(iterable)
:等到所有 promise 都已敲定(每个 promise 都已兑现或已拒绝),如果想知道每个promise的结果时可使用
示例:
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).then((results) => results.forEach((result) => console.log(result.status)));
// expected output:
// "fulfilled"
// "rejected"