# Promise
Promise 对象是一个构造函数,用来生成 Promise 实例
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise 新建后就会立即执行传入的函数,并且已同步的方式执行
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
# resolve方法
当调用 resolve()
方法, Promise 的状态将从 pending
置为 fulfilled
,resolve()
可传递参数,该参数会被 Promise 实例的 then
方法的每个参数接收。
注意当 resolve()
传递的参数是一新的个 Promise 时,那么原来的 Promise 状态将会被这个新的 Promise 参数的影响
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
// Error: fail
上面代码中,p1
是一个 Promise,3 秒之后变为 rejected
。p2
的状态在 1 秒之后改变,resolve
方法返回的是 p1
。由于 p2
返回的是另一个 Promise,导致 p2
自己的状态无效了,由 p1
的状态决定 p2
的状态。所以,后面的 then
语句都变成针对后者(p1
)。又过了 2 秒,p1
变为 rejected
,导致触发 catch
方法指定的回调函数
调用 resolve
或 reject
并不会终结 Promise 的参数函数的执行
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
// 2
// 1
# reject方法
当调用 resolve()
方法, Promise 的状态将从 pending
置为 rejected
# Promise.prototype.then()
then()
的作用是为 Promise 实例添加状态改变时的回调函数。 then
方法的第一个参数是 resolved
状态的回调函数,第二个参数(可选)是 rejected
状态的回调函数
then
方法返回的是一个新的 Promise 实例(注意,不是原来那个 Promise 实例)。因此可以采用链式写法,即 then
方法后面可以再调用 then
方法
function fetch(time){
return new Promise((resolve, reject) => {
setTimeout(resolve, time)
})
}
const p1 = new Promise(function (resolve, reject) {
fetch(2000, true)
.then(res => {
resolve('data1')
})
})
p1.then(res => {
console.log(res) // 2秒后 打印 data1
return fetch(1000)
})
.then(res => {
console.log(res) // 再过1秒后 打印 undefind
return 'end'
})
.then(res => {
console.log(res) // 基本跟上一次打印同时输出 'end'
})
# Promise.prototype.catch()
Promise.prototype.catch()
方法是 .then(null, rejection)
或 .then(undefined, rejection)
的别名,用于指定发生错误时的回调函数
p.then((val) => console.log('fulfilled:', val))
.catch((err) => console.log('rejected', err));
// 等同于
p.then((val) => console.log('fulfilled:', val))
.then(null, (err) => console.log("rejected:", err));
catch
不仅可以接收 Promise 内部执行 reject
方法触发的错误,也可以接收 then
方法中错误
Promise.resolve('执行成功')
.then(res => {
console.log(res) // 执行成功
throw Error("then 错误")
})
.then(res => {
console.log(res)
return 'end'
})
.catch(err => {
console.log('err1:', err) // err1: Error then 错误
return "catch 继续走"
})
.then(res => {
console.log('then', res) // thenr catch 继续走
throw Error("then 错误2")
})
.catch(err => {
console.log('err2:', err) // err1: Error then 错误
return Promise.reject('catch2')
})
.catch(err => {
console.log('err3:', err) // catch2
})
从上面的例子可以看到几点规律:
catch
方法也可以支持链式调用无论是在
Promise
中还是在then
,catch
中,抛出的错误总能被后面catch
捕获catch
的return
的值会被后面的then
方法继续接收
如果 Promise 状态已经变成 resolved
,再抛出错误是无效的
const promise = new Promise(function(resolve, reject) {
resolve('ok');
throw new Error('test');
});
promise
.then(function(value) { console.log(value) })
.catch(function(error) { console.log(error) });
// ok
如果没有使用 catch()
方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行会报错,因为x没有声明
resolve(x + 2);
});
};
someAsyncThing().then(function() {
console.log('everything is great');
});
setTimeout(() => { console.log(123) }, 2000);
// Uncaught (in promise) ReferenceError: x is not defined
// 123
# Promise.prototype.finally()
finally()
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作, 并且返回一个设置了 finally
回调函数的 Promise 对象
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
finally
方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是 fulfilled
还是 rejected
。这表明,finally
方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果
Promise.resolve('执行成功')
.then(res => {
console.log(res) // 执行成功
return 'then'
})
.finally(() => {
console.log('finally') // finally
return 'F'
})
.then(res => {
console.log(res') // then
})
finally
后面可以链式调用then
方法收接前面的resolve
任务,当然也可以使用catch
方法收接前面的reject
错误
finally()
虽然与 .then(onFinally, onFinally)
类似,它们不同的是:
由于无法知道
Promise
的最终状态,所以finally
的回调函数中不接收任何参数,它仅用于无论最终结果如何都要执行的情况与
Promise.resolve(2).then(() => {}, () => {})
(resolved
的结果为undefined
)不同,Promise.resolve(2).finally(() => {})
resolved
的结果为2
。同样,
Promise.reject(3).then(() => {}, () => {})
(resolved
的结果为undefined
),Promise.reject(3).finally(() => {}
)rejected
的结果为3
# Promise.resolve()
Promise.resolve()
等价于下面的写法
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
Promise.resolve
方法的参数分成四种情况
参数是一个 Promise 实例
如果参数是 Promise 实例,那么 Promise.resolve
将不做任何修改、原封不动地返回这个实例
参数是一个 thenable
对象
thenable
对象指的是具有 then
方法的对象,比如下面这个对象
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
}
Promise.resolve
方法会将这个对象转为 Promise 对象,然后就立即执行 thenable
对象的 then
方法。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
参数不是具有then方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有 then
方法的对象,则 Promise.resolve
方法返回一个新的 Promise 对象,状态为 resolved
const p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
不带有任何参数
Promise.resolve()
方法允许调用时不带参数,直接返回一个 resolved
状态的 Promise 对象
# Promise.reject()
Promise.reject(reason)
方法也会返回一个新的 Promise 实例,该实例的状态为 rejected
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log(s)
});
// 出错了
# Promise.all()
Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
const p = Promise.all([p1, p2, p3])
上面例子中 p
的状态由 p1
、p2
、p3
决定
只有
p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数只要
p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数
Promise.all([Promise.resolve(), Promise.reject('err')])
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err) // err
})
如果作为参数的 Promise 实例,自己定义了 catch
方法,那么它一旦被 rejected
,并不会触发 Promise.all()
的 catch
方法
const catchPro = Promise.reject('err')
.catch(err => console.log(err))
Promise.all([Promise.resolve('res'), catchPro])
.then(res => {
console.log(res) // ["res", undefined]
})
.catch(err => {
console.log(err)
})
因为 catchPro
定义了 catch
, 所以不会触发 Promise.all()
的 catch
方法, 因为 catchPro.catch
返回也是一个 Promise
, 所以上面 Promise.all
每个 Promise 实例实际上是 catchPro.catch
返回的 Promise
const catchPro = Promise.reject('err')
.catch(err => {
throw Error('err')
})
Promise.all([Promise.resolve('res'), catchPro])
.then(res => {
console.log(res)
})
.catch(err => {
console.log('err', err) // err Error: err
})
如果 catch
内抛出错误且当前 Promise 实例后面没有 catch
接收,这时才会被 Promise.all
的 catch
接收
# Promise.race()
Promise.race()
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
const p = Promise.race([p1, p2, p3]);
上面代码中,只要 p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p
的回调函数
function fetch(time){
return new Promise((resolve, reject) => {
setTimeout(resolve(10), time)
})
}
const catchPro = Promise.reject('err')
Promise.race([catchPro, fetch(1000)])
.then(res => {
console.log(res)
})
.catch(err => {
console.log('err', err) // err, err
})
下面是一个例子,如果指定时间内没有获得结果,就将 Promise 的状态变为 reject
,否则变为 resolve
const p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
上面代码中,如果 5
秒之内 fetch
方法无法返回结果,变量p的状态就会变为 rejected
,从而触发 catch
方法指定的回调函数
# Promise.allSettled()
Promise.allSettled()
方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是 fulfilled
还是 rejected
,包装实例才会结束。该方法由 ES2020 引入
Promise.allSettled([fetch(2000, false), fetch(1000)])
.then(res => {
console.log(res)
})
.catch(err => {
console.log('err', err)
})
上面的代码将在 3 秒后输出结果:
0: {status: "rejected", reason: 10}
1: {status: "fulfilled", value: 10}
返回数组对象都有 status
属性,该属性的值只可能是字符串 fulfilled
或字符串 rejected
fulfilled
时,对象有value
属性rejected
时,对象有reason
属性
该方法返回的新的 Promise 实例,一旦结束,状态总是 fulfilled
,不会变成 rejected
# Promise.any()
Promise.any()
方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只要参数实例有一个变成 fulfilled
状态,包装实例就会变成 fulfilled
状态;如果所有参数实例都变成 rejected
状态,包装实例就会变成 rejected
状态。该方法目前是一个第三阶段的提案
Promise.any()
跟 Promise.race()
方法很像,只有一点不同,就是不会因为某个 Promise 变成 rejected
状态而结束
function fetch(time, sue = true){
return new Promise((resolve, reject) => {
let callback = sue ? resolve : reject
setTimeout(() => {
callback(10)
}, time)
})
}
// 待验证,浏览器目前不支持此方法
Promise.any([fetch(1000, false), fetch(1000)])
.then(res => {
console.log(res)
})
.catch(err => {
console.log('err', err) // err, err
})
Promise.any() 抛出的错误,不是一个一般的错误,而是一个 AggregateError
实例。它相当于一个数组,每个成员对应一个被 rejected
的操作所抛出的错误
// 待验证
# Promise.try()
// todo
# 应用
Generator 函数与 Promise 的结合
使用 Generator 函数管理流程,遇到异步操作的时候,通常返回一个Promise对象
function getFoo () {
return new Promise(function (resolve, reject){
resolve('foo');
});
}
const g = function* () {
try {
const foo = yield getFoo();
console.log(foo);
} catch (e) {
console.log(e);
}
};
function run (generator) {
const it = generator();
function go(result) {
if (result.done) return result.value;
return result.value.then(function (value) {
return go(it.next(value));
}, function (error) {
return go(it.throw(error));
});
}
go(it.next());
}
run(g);
上面代码的 Generator 函数 g
之中,有一个异步操作 getFoo
,它返回的就是一个 Promise 对象。函数run
用来处理这个 Promise 对象,并调用下一个 next
方法。
# Promise实现
下面是 Promise 的简单实现
class myPromise{
constructor(handle) {
this.status = 'PENDING'
try{
handle(this.resolve.bind(this), this.reject.bind(this))
} catch (e){
this.reject(e)
}
this.doResolve = null
this.doReject = null
}
resolve(val){
if(this.status !== 'PENDING') return
this.status === 'FULFILLED'
queueMicrotask(() => {
this.doResolve(val)
})
}
reject(val){
if(this.status !== 'PENDING') return
this.status === 'REJECTED'
queueMicrotask(() => {
this.doReject(val)
})
}
then(fn) {
this.doResolve = fn
}
catch(fn) {
this.doReject = fn
}
}
var pro = new myPromise((resolve, reject) => {
setTimeout(() => {resolve('执行成功')}, 2000)
})
pro.then(res => {console.log(res)})
使用
queueMicrotask
将then
的执行加入到微任务队列中handle(this.resolve.bind(this), this.reject.bind(this))
,传递自己的resolve
和reject
实例给外面的使用
# 支持多个then
class myPromise{
constructor(handle) {
this.status = 'PENDING'
try{
handle(this.resolve.bind(this), this.reject.bind(this))
} catch (e){
this.reject(e)
}
this.doResolve = []
this.doReject = null
}
resolve(val){
if(this.status !== 'PENDING') return
this.status === 'FULFILLED'
queueMicrotask(() => {
this.doResolve.forEach(itemFn => {
itemFn(val)
})
})
}
reject(val){
if(this.status !== 'PENDING') return
this.status === 'REJECTED'
queueMicrotask(() => {
this.doReject(val)
})
}
then(fn) {
this.doResolve.push(fn)
}
catch(fn) {
this.doReject = fn
}
}
var pro = new myPromise((resolve, reject) => {
setTimeout(() => {resolve('执行成功')}, 2000)
})
pro.then(res => {console.log(res)})
pro.then(res => {console.log('then2'+res)})
# then返回promise
class myPromise{
constructor(handle) {
this.status = 'PENDING'
try{
handle(this.resolve.bind(this), this.reject.bind(this))
} catch (e){
this.reject(e)
}
this.doResolve = []
this.doReject = null
this.sonResovle = null
this.sonReject = null
}
resolve(val){
if(this.status !== 'PENDING') return
this.status === 'FULFILLED'
queueMicrotask(() => {
this.doResolve.forEach(itemFn => {
itemFn(val)
this.sonResovle(val)
})
})
}
reject(val){
if(this.status !== 'PENDING') return
this.status === 'REJECTED'
queueMicrotask(() => {
this.doReject(val)
})
}
then(fn) {
this.doResolve.push(fn)
const _this = this
return new myPromise((resolve, reject) => {
_this.sonResovle = resolve
_this.sonReject = reject
})
}
catch(fn) {
this.doReject = fn
}
}
var pro = new myPromise((resolve, reject) => {
setTimeout(() => {resolve('执行成功')}, 2000)
})
pro.then(res => {console.log(res)})
pro.then(res => {console.log('then2'+res)})
.then(res => console.log('sonThen' + res))
.then(res => console.log('sonThen2' + res))
← Map和WeakMap Iterator →