Promise
语法
const promise = new Promise(function(resolve, reject) {
// executor(执行函数)
})
传递给 new Promise的函数称之为 executor。当 promise 被创建时,它会被自动调用并会产生一个结果。
Promise执行过程中总共会有3种状态:
- pending 表明正在执行任务
- fulfilled 表明任务完成
- rejected 表明有错误发生
Promise的状态只会从pending -> fulfilled 或者 pending -> rejected,fulfilled与rejected不能相互转换,且过程不可逆。
当 executor 完成任务时,应调用下列的方法之一:
- resolve(value) —— 任务已经完成 (将 state 设置为 "fulfilled",将 result 设置为 value)
- reject(error) —— 表明有错误发生 (将 state 设置为 "rejected",将 result 设置为 error)
要点
- executor 应该完成任务(一般需要时间),然后调用 resolve 或 reject 来改变 promise 对象的对应状态。
- executor 只会调用 resolve 或 reject。Promise 的最后状态一定会变化。
示例
const promise = new Promise(function(resolve, reject) {
// 当 promise 被构造时,函数会自动执行
// 在 1 秒后,任务完成
setTimeout(() => resolve("done!"), 1000)
})
const promise2 = new Promise(function(resolve, reject) {
// 在 1 秒后,任务失败
setTimeout(() => reject(new Error("error!")), 1000)
})
".then"和".catch"
语法:
promise.then(
function(result) { /* handle a successful result */ },
function(error) { /* handle an error */ }
)
- 第一个函数参数在 promise 为 resolved 时被解析,然后得到结果并运行。第二个参数在状态为 rejected 并得到错误时使用。
示例
/* 成功 */
const promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve("done!"), 1000)
})
// resolve 在 .then 中运行第一个函数
promise.then(
result => console.log(result), // 在 1 秒后显示“done!”
error => console.log(error) // 不会运行
)
/* 错误 */
const promise2 = new Promise(function(resolve, reject) {
setTimeout(() => reject(new Error("error!")), 1000)
})
// reject 在 .then 中运行第二个函数
promise2.then(
result => console.log(result), // 无法运行
error => console.log(error) // 在 1 秒后显示 "Error: error!"
)
我们可以只为“.then”提供一个参数,只得到成功的结果promise.then()
, 如果要捕获错误,可以将第一个参数传为null,交由第二个函数处理promise.then(null, function)
, 或者用一种更直观清晰的写法是用“.catch”promise.catch()
promise.then(res => {
// 处理成功结果
}).catch(err => {
// 处理错误结果
})
// .catch(f) 是 .then(null, f) 的模拟, 是一种简写
.then和.catch都是异步的
当 .then/catch 处理器应该执行时,它会首先进入内部队列。JavaScript 引擎从队列中提取处理器,并在当前代码完成时执行。
const promise = new Promise(resolve => resolve("done!"))
promise.then(console.log) // 完成!(在当前代码完成之后)
console.log("code finished") // 先显示
// code finished
// done!
链式调用
.then/catch(handler) 返回一个新的 promise,它根据处理程序的作用而改变
- 如果它返回一个值或在没有 return(同 return undefined)的情况下结束,则新的 promise 将变为 resolved,并且用该值作参数调用最近的 resolve 处理程序(.then 的第一个参数)。
- 如果它抛出错误,则新的 promise 将 rejected,并且用该错误作参数调用最接近的 reject 处理程序(.catch 或 .then 的第二个参数)
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000)
}).then(res => {
console.log(res) // 1
return res * 2
}).then(res => {
console.log(res); // 2
return res * 2
}).then(res => {
console.log(res) // 4
}).then(res => {
console.log(res) // undefined
throw new Error('error')
}).catch(err => {
console.log(err) // Error: error
})
.catch
并不是始终在链的结束处,我们应该准确的放置在我们想要处理错误的地方。
Promise API
在 Promise 类中,有 4 中静态方法。
Promise.resolve()
语法:
const promise = Promise.resolve(value)
// 根据给定的 value 值返回 resolved promise
/*
* 与下述语法等价
*/
const promise2 = new Promise(resolve => resolve(value))
一般会用于封装某个方法,确保函数会返回一个promise
Promise.reject()
语法:
const promise = Promise.reject(value)
// 创建一个带有 error 的 rejected promise。
/*
* 与下述语法等价
*/
const promise2 = new Promise((resolve, reject) => reject(value))
Promise.all()
该方法并行运行多个 promise,并等待所有 promise 完成
语法:
const promise = Promise.all(iterable)
// iterable 是一个包含promise可迭代对象,一般都是数组
示例:
Promise.all([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 3000)), // 1
new Promise((resolve, reject) => setTimeout(() => resolve(2), 2000)), // 2
new Promise((resolve, reject) => setTimeout(() => resolve(3), 1000)) // 3
]).then(console.log)
// [ 1, 2, 3 ]
Promise.all()
中传参与出参顺序是相同的。所以此处虽然第一个Promise需要完成的时间比其他长,但仍然是结果数组中的第一个。
如果任何 promise 为 rejected,Promise.all 就会立即以 error reject。
Promise.all([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("error!")), 2000)),
new Promise((resolve, reject) => setTimeout(() => console.log(3), 3000))
]).catch(console.log)
// Error: error!
// 3
这里的第二个 promise 在两秒内为 reject。这立即导致了对 Promise.all 的 reject,因此 .catch 被执行。但是:其他的promise会继续执行,只是结果会被忽略。
Promise.race()
与 Promise.all 类似,但不会等待所有promise都完成 —— 只等待第一个完成(或者有 error),然后继续执行。
语法:
const promise = Promise.race(iterable)
示例:
Promise.race([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("error!")), 2000)),
new Promise((resolve, reject) => setTimeout(() => console.log(3), 3000))
]).then(console.log)
// 1
// 3
/* race 就是竞赛的意思,返回结果最快的那一个就是第一个。*/
第一个结果或者错误会成为整个 Promise.race 的结果 第一个promise得到处理后,其他的所有结果或者错误都会被忽略。
Promise.all与Promise.race的比较:
- 两者接收的参数类型一致
- 两者都会执行完所有的promise
Promise.all
必须等到所有promise都成功才会返回结果数组,其中若有一个reject,则整个Promise.all就会reject。Promise.race
返回的永远是执行最快的那个promise的结果或者错误。
问题:Promise.all与Promise.race都无法拿到所有的返回结果(包括错误的结果),这在我们的现实开发中其实是存在问题的,因为我们往往想要拿到所有的结果,特别是在与后台的通信中。以下为解决方案:
点击查看代码
const taskIds = [1, 3, 4, 6, 8]
const taskQueue = taskIds.map(id => ajax(id).catch(err => err)) // 这里的.catch为关键操作
Promise.all(taskQueue).then(res => {
console.log(res)
}).catch(err => {
console.error(err)
})
/* 模拟异步操作 */
function ajax(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id === 6) {
reject(new Error('error~'))
}
resolve({'id': id, 'name': id + 'ddd'})
}, 1000*id)
})
}
// resolve与reject的结果都会被返回
/**
* [
* { id: 1, name: '1ddd' },
{ id: 3, name: '3ddd' },
{ id: 4, name: '4ddd' },
Error: error~
at Timeout.setTimeout [as _onTimeout] (D:\test-demo\somedemo\promise.js:86:16)
at ontimeout (timers.js:436:11)
at tryOnTimeout (timers.js:300:5)
at listOnTimeout (timers.js:263:5)
at Timer.processTimers (timers.js:223:10),
{ id: 8, name: '8ddd' } ]
*/
要点:我们不能改变 Promise.all 的执行方式:如果它检测到 error,就会 reject 它。因此我们需要避免任何 error 发生。即如果发生error,我们仍然要将它返回。
- .catch 会对所有的 promise 产生 error,然后正常返回。根据 promise 的执行方式,只要 .then/catch 处理器返回值(无论是 error 对象或其他内容),执行流程就会“正常”进行。
- 因此 .catch 会将 error 作为“正常”结果返回给外部的 Promise.all。
Promise.allSettled()
Promise.allSettled()
能拿到所有promise执行完成的结果,无论每个promise是resolve还是reject;因此它可以用于想要得到所有promise执行结果的场景,而不是像Promise.all方法那样只要有任意一个promise被reject就无法拿到最后的结果。
Promise.any()
Promise.any()
返回第一个被resolve的值,只要有一个promise被resolve,它就会立即返回,所有它不会等待其他的promise完成;也就是说该方法忽略所有被reject的promise,直到第一个被resolve的promise。
Promise模拟实现
点击查看代码
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
try {
executor(this._resolve, this._reject)
} catch (e) {
this._reject(e)
}
}
state = PENDING
result = undefined
reason = ''
fulfilledCallbacks = []
rejectedCallbacks = []
_resolve = value => {
if (this.state !== PENDING) return
this.state = FULFILLED
this.result = value
this.fulfilledCallbacks.forEach(cb => cb())
}
_reject = reason => {
if (this.state !== PENDING) return
this.state = REJECTED
this.reason = reason
this.rejectedCallbacks.forEach(cb => cb())
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
const self = this
const promise2 = new MyPromise((resolve, reject) => {
if (self.state === FULFILLED) {
setTimeout(() => {
try {
const res = onFulfilled(self.result)
self._resolvePromise(promise2, res, resolve, reject)
} catch (e) {
reject(e)
}
})
} else if (self.state === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(self.reason)
self._resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
} else if (self.state === PENDING) {
self.fulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const res = onFulfilled(self.result)
self._resolvePromise(promise2, res, resolve, reject)
} catch (e) {
reject(e)
}
})
})
self.rejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(self.reason)
self._resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
return promise2
}
catch(onRejected) {
return this.then(null, onRejected)
}
_resolvePromise(promise, x, resolve, reject) {
console.log(x, '_resolvePromise inner')
if (promise === x) {
console.log('chaining cycle')
return reject(new TypeError('chaining cycle'))
}
if (x instanceof MyPromise) {
try {
x.then(resolve, reject)
} catch (e) {
reject(e)
}
} else {
resolve(x)
}
}
static resolve(param) {
if (param instanceof MyPromise) {
return param
}
return new MyPromise(resolve => resolve(param))
}
static reject(reason) {
return new MyPromise((resolve, reject) => reject(reason))
}
static all(promises) {
return new MyPromise((resolve, reject) => {
const length = promises.length
let index = 0
const result = []
for(let i = 0; i < length; i++) {
MyPromise.resolve(promises[i]).then(res => {
index++
result[i] = res
if (index === length) {
resolve(result)
}
}, reason => {
reject(reason)
})
}
})
}
static allSettled(promises) {
return new MyPromise((resolve, reject) => {
const length = promises.length
let index = 0
const result = []
for(let i = 0; i < length; i++) {
MyPromise.resolve(promises[i]).then(res => {
index++
result[i] = {
status: 'fulfilled',
value: res
}
if (index === length) {
resolve(result)
}
}, reason => {
index++
result[i] = {
status: 'rejected',
reason
}
if (index === length) {
resolve(result)
}
})
}
})
}
static race(promises) {
return new MyPromise((resolve, reject) => {
for(const promise of promises) {
MyPromise.resolve(promise).then(res => {
resolve(res)
}, reason => {
reject(reason)
})
}
})
}
}
测试
打开控制台,点击下方Result,查看console