Open kangkai124 opened 5 years ago
作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法。
const p1 = new Promise((resolve, reject) => {
resolve('hello');
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了');
})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 报错了]
上面代码中,p1
会resolved
,p2
首先会rejected
,但是p2
有自己的catch
方法,该方法返回的是一个新的 Promise 实例,p2
指向的实际上是这个实例。该实例执行完catch
方法后,也会变成resolved
,导致Promise.all()
方法参数里面的两个实例都会resolved
,因此会调用then
方法指定的回调函数,而不会调用catch
方法指定的回调函数。
如果p2
没有自己的catch
方法,就会调用Promise.all()
的catch
方法。
<button id="start">开始一个promise</button>
<button id="cancel">取消它</button>
let cancelReason = 'cancel'
let controller
function race(p) {
let obj = {}
let p1 = new Promise((resolve, reject) => {
obj.resolve = resolve
obj.reject = reject
})
obj.promise = Promise.race([p, p1])
return obj
}
document.querySelector('#start').onclick = function () {
console.log('开始');
let _promise = new Promise(resolve => {
setTimeout(() => {
resolve('hello')
}, 5000)
})
controller = race(_promise)
controller.promise.then(resolveValue => {
if (resolveValue === cancelReason) return
// resolve code
console.log('resolve! ' + resolveValue)
}, rejectValue => {
if (rejectValue === cancelReason) return
console.log('reject! ' + rejectValue)
})
}
document.querySelector('#cancel').onclick = function () {
console.log('取消了')
controller.resolve(cancelReason)
}
解决了异步问题,相对于之前的解决方案—回调函数和事件,更加的合理和强大。
简版:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class Aromise {
constructor(fn) {
this.state = PENDING
this.value = null
this.reason = null
this.onFulfilledCbs = []
this.onRejectedCbs = []
const resolve = value => {
// 使用macro-task机制,确保onFulfilled异步执行,
// 且在 then 方法被调用的那一轮事件循环之后的新执行栈中执行,
// 即将then的回调push到onFulfilledCbs后再执行。
setTimeout(() => {
if (this.state === PENDING) {
this.state = FULFILLED
this.value = value
this.onFulfilledCbs.forEach(cb => {
this.value = cb(this.value)
})
}
})
}
const reject = reason => {
setTimeout(() => {
if (this.state === PENDING) {
this.state = REJECTED
this.reason = reason
this.onRejectedCbs.forEach(cb => {
this.reason = cb(this.reason)
})
}
})
}
try {
fn(resolve, reject)
} catch (err) {
reject(err)
}
}
then (onFulfilled, onRejected) {
typeof onFulfilled === 'function' && this.onFulfilledCbs.push(onFulfilled)
typeof onRejected === 'function' && this.onRejectedCbs.push(onRejected)
return this
}
}
new Aromise((resolve, reject) => {
console.log('start');
setTimeout(() => {
resolve(223)
}, 2000)
})
.then(res => {
console.log(res)
})
如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。
let t1 = new Promise((resolve,reject)=>{
resolve("t1-success")
})
let t2 = new Promise((resolve,reject)=>{
resolve("t2-success")
})
let t3 =Promise.reject("t3-error");
Promise.all([t1,t2,t3]).then(res=>{
console.log(res)
}).catch(error=>{
console.log(error)
})
//打印出来是t3-error
Error 对象是ECMAScript的内建(build in)对象。
但是由于stack trace等原因我们不能完美的创建一个继承自 Error 的类,不过在这里我们的目的只是为了和Error有所区别,我们将创建一个 TimeoutError 类来实现我们的目的。
在ECMAScript6中可以使用 class 语法来定义类之间的继承关系。
class MyError extends Error{
// 继承了Error类的对象
}
为了让我们的 TimeoutError 能支持类似 error instanceof TimeoutError 的使用方法,我们还需要进行如下工作。
//TimeoutError.js
function copyOwnFrom(target, source) {
Object.getOwnPropertyNames(source).forEach(function (propName) {
Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName));
});
return target;
}
function TimeoutError() {
var superInstance = Error.apply(null, arguments);
copyOwnFrom(this, superInstance);
}
TimeoutError.prototype = Object.create(Error.prototype);
TimeoutError.prototype.constructor = TimeoutError;
我们定义了 TimeoutError 类和构造函数,这个类继承了Error的prototype。
它的使用方法和普通的 Error 对象一样,使用 throw 语句即可,如下所示。
var promise = new Promise(function () {
throw TimeoutError('timeout')
})
promise.catch(function (error) {
console.log(error instanceof TimeoutError) // true
})
有了这个 TimeoutError 对象,我们就能很容易区分捕获的到底是因为超时而导致的错误,还是其他原因导致的Error对象了。
catch
返回的还是一个 Promise 对象,因为后面还可以接着调用then
方法。