Promise 的出现就是为了解决异步编程回调地狱问题的
本文我们一起来学习一下 Promise 相关用法并且我们一起手写一个符合 Promise/A+ 规范的 Promise
promise 是一个对象,保存着某个未来才会结束的事件
对象的状态不受外界影响
Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise
这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
一旦状态改变,就不会再变,任何时候都可以得到这个结果
Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同。事件的特点是:如果你错过了它,再去监听,是得不到结果的。
Promise
,一旦新建它就会立即执行,无法中途取消。Promise
内部抛出的错误,不会反应到外部。pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。promise.then:回调函数延迟绑定
返回resolve
const promise = new Promise((resolve, reject) => {
resolve(100)
})
promise.then((value) => {
console.log('resolved', value) // resolve 100
},(error) => {
console.log('rejected', error)
})
返回reject
const promise = new Promise((resolve, reject) => {
reject(new Error('promise rejected'))
})
promise.then((value) => {
console.log('resolved', value)
},(error) => {
console.log('rejected', error)
// rejected Error: promise rejected
// at E:\professer\lagou\Promise\promise-example.js:4:10
// at new Promise ()
})
常见误区*:*** 嵌套使用的方式是使用Promise最常见的误区。要使用promise的链式调用的方法尽可能保证异步任务的扁平化。
链式调用的理解:
then中回调的onRejected方法
.catch()(推荐)
promise中如果有异常,都会调用reject方法,还可以使用.catch(); 使用.catch方法更为常见,因为更加符合链式调用
.catch形式和前面then里面的第二个参数的形式,两者异常捕获的区别:
.catch()是对上一个.then()返回的promise进行处理,不过第一个promise的报错也顺延到了catch中,而then的第二个参数形式,只能捕获第一个promise的报错,如果当前then的resolve函数处理中有报错是捕获不到的。
所以.catch是给整个promise链条注册的一个失败回调。推荐使用
ajax('/api/user.json')
.then(function onFulfilled(res) {
console.log('onFulfilled', res)
}).catch(function onRejected(error) {
console.log('onRejected', error)
})
// 相当于
ajax('/api/user.json')
.then(function onFulfilled(res) {
console.log('onFulfilled', res)
})
.then(undefined, function onRejected(error) {
console.log('onRejected', error)
})
全局对象上的unhandledrejection事件
在全局对象上注册一个unhandledrejection事件,处理那些代码中没有被手动捕获的promise异常,当然并不推荐使用
更合理的是:在代码中明确捕获每一个可能的异常,而不是丢给全局处理
// 浏览器
window.addEventListener('unhandledrejection', event => {
const { reason, promise } = event
console.log(reason, promise)
//reason => Promise 失败原因,一般是一个错误对象
//promise => 出现异常的Promise对象
event.preventDefault()
}, false)
// node
process.on('unhandledRejection', (reason, promise) => {
console.log(reason, promise)
//reason => Promise 失败原因,一般是一个错误对象
//promise => 出现异常的Promise对象
})
快速的将一个值转换为 Promise 对象
快速创建一个一定是失败的 Promise 对象
接收一个数组
返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝
回调队列中的任务称为 宏任务,宏任务执行过程中可以临时加上一些额外需求,这些额外的需求可以选择作为一个新的宏任务进到队列中排队,也可以作为当前任务的 微任务(直接在当前任务执行过后立即执行),Promise 的回调会作为微任务进行执行
微任务就是为了提高整体的响应能力
举个栗子
有一个大爷去银行存款,排队等到他去存款的时候突然想要开通手机银行,这时候银行柜员会优先完成大爷的需求再开始其他人的业务。
大爷去存款:宏任务
大爷要开通手机银行: 微任务
常见的宏任务: setTimeout, setInterval…
常见的微任务: promise.then, process.nextTick、MutationObserver
官方地址:Promises/A+
以下为摘录的一些关键部分
then
method whose behavior conforms to this specification. 【promise是一个具有then 方法的对象或者方法,他的行为符合该规范】then
method.【thenable是一个定义了then方法的对象或者方法】undefined
, a thenable, or a promise).【value是任何一个合法的JS值(包括 undefined、thenable 或 promise)】throw
statement.【exception是一个被throw语句抛出的值-异常】A promise must be in one of three states: pending, fulfilled, or rejected.【一个 Promise 有三种状态:pending、fulfilled 和 rejected】
When pending, a promise:
When fulfilled, a promise:
When rejected, a promise:
Here, “must not change” means immutable identity (i.e. ===
), but does not imply deep immutability.
then
Method【then 方法】A promise must provide a then
method to access its current or eventual value or reason.【一个 Promise 必须拥有一个 then 方法来访问它的值或者拒绝原因。】
A promise’s then
method accepts two arguments:【then 方法有两个参数】
promise.then(onFulfilled, onRejected)
Both onFulfilled
and onRejected
are optional arguments:
onFulfilled
is not a function, it must be ignored.onRejected
is not a function, it must be ignored.If onFulfilled
is a function:
promise
is fulfilled, with promise
’s value as its first argument.promise
is fulfilled.If onRejected
is a function,
promise
is rejected, with promise
’s reason as its first argument.promise
is rejected.then
may be called multiple times on the same promise.
promise
is fulfilled, all respective onFulfilled
callbacks must execute in the order of their originating calls to then
.promise
is rejected, all respective onRejected
callbacks must execute in the order of their originating calls to then
.then
must return a promise [3.3].
promise2 = promise1.then(onFulfilled, onRejected);
onFulfilled
or onRejected
returns a value x
, run the Promise Resolution Procedure [[Resolve]](promise2, x)
.onFulfilled
or onRejected
throws an exception e
, promise2
must be rejected with e
as the reason.onFulfilled
is not a function and promise1
is fulfilled, promise2
must be fulfilled with the same value as promise1
.onRejected
is not a function and promise1
is rejected, promise2
must be rejected with the same reason as promise1
.首先分析其原理
- promise 就是一个类: 在执行类的时候需要传递一个执行器,执行器会立即执行
- promise 的三种状态: pending, fulfilled, or rejected
- resolve 和 reject 方法用来更改状态
resolve -> fulfilled / reject -> rejected- then 方法判断状态:
成功状态 -> 调用成功回调, 失败状态 -> 调用失败回调
// 三种状态
const PENDING = 'pending', FULFILLED = 'fulfilled', REJECTED = 'rejected'
class MyPromise {
constructor(excutor) {
excutor(this.resolve, this.reject);
}
status = PENDING; // 当前的状态
value = undefined; // 成功的值
reason = undefined; // 失败的值
resolve = (value) => {
// must not transition to any other state
if (this.status !== PENDING) return
this.status = FULFILLED;
this.value = value
}
reject = (reason) => {
// must not transition to any other state
if (this.status !== PENDING) return
this.status = REJECTED;
this.reason = reason
}
then(onFulfilled, onRejected) {
switch (this.status) {
case FULFILLED:
onFulfilled(this.value);
break;
case REJECTED:
onRejected(this.reason);
break;
}
}
}
test
const Promise = require('./index');
const res_1 = new Promise(function(resolve, reject) {
// resolve('最简单的Promise:resolve');
reject('最简单的Promise:reject')
})
res_1.then(res => {
console.log('success', res)
}, err => {
console.log('rejected', err)
})
第一种情况下没有处理到异步的情况;异步情况的处理其实就是then方法中对应pending状态的处理,如下所示:
class MyPromise {
// 添加相应值
onFulfilled = undefined; // 存储成功后的回调
onRejected = undefined; // 存储失败后的回调
resolve = (value) => {
// must not transition to any other state
if (this.status !== PENDING) return
this.status = FULFILLED;
this.value = value;
// 成功之后立即执行缓存中的回调
this.onFulfilled && this.onFulfilled(this.value)
}
reject = (reason) => {
// must not transition to any other state
if (this.status !== PENDING) return
this.status = REJECTED;
this.reason = reason
this.onRejected && this.onRejected(this.reason)
}
then(onFulfilled, onRejected) {
switch (this.status) {
case FULFILLED:
onFulfilled(this.value);
break;
case REJECTED:
onRejected(this.reason);
break;
default: // pending状态
this.onFulfilled = onFulfilled
this.onRejected = onRejected
}
}
}
test
const res_2 = new Promise(function(resolve, reject) {
setTimeout(() => {
// resolve('异步Promise:resolve');
reject('异步Promise:reject')
}, 100)
})
res_2.then(res => {
console.log('success', res)
}, err => {
console.log('rejected', err)
})
我们来看以下的使用:
通过上述的这种调用方式【多次调用】我们可以看到右侧的结果显示,当同步Promise的情况下,输出正常,异步Promise下只会处理第一个then,这是不对的,现在我们开始完善以上使用方式
class MyPromise {
// 修改回调为数组形式
onFulfilled = []; // 存储成功后的回调
onRejected = []; // 存储失败后的回调
resolve = (value) => {
...
// 循环取出调用成功方法
while(this.onFulfilled.length) this.onFulfilled.shift()(this.value)
}
reject = (reason) => {
...
while(this.onRejected.length) this.onRejected.shift()(this.reason)
}
then(onFulfilled, onRejected) {
switch (this.status) {
case FULFILLED:
onFulfilled(this.value);
break;
case REJECTED:
onRejected(this.reason);
break;
default: // pending状态
this.onFulfilled.push(onFulfilled)
this.onRejected.push(onRejected)
}
}
}
test
const res_3 = new Promise(function(resolve, reject) {
// resolve('同步Promise:resolve');
// reject('同步Promise:reject')
setTimeout(() => {
// resolve('异步Promise:resolve');
reject('异步Promise:reject')
}, 100)
})
res_3.then(val => {
console.log('one', val)
}, err => {
console.log('one', err)
})
res_3.then(val => {
console.log('two', val)
}, err => {
console.log('two', err)
})
可以链式调用的核心就是then 返回一个promise
class MyPromise {
...
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
let func = undefined;
switch (this.status) {
case FULFILLED:
func = onFulfilled(this.value);
resolvePromise(func, resolve, reject)
break;
case REJECTED:
func = onRejected(this.reason);
resolvePromise(func, resolve, reject)
break;
default: // pending状态
this.onFulfilled.push(() => {
func = onFulfilled(this.value)
resolvePromise(func, resolve, reject)
})
this.onRejected.push(() => {
func = onRejected(this.reason)
resolvePromise(func, resolve, reject)
})
}
})
}
}
function resolvePromise(func, resolve, reject) {
// 返回promise
if (func instanceof MyPromise) {
func.then(resolve, reject)
} else {
resolve(func)
}
}
test
const res_4 = new Promise(function(resolve, reject) {
// resolve('同步Promise:resolve');
// reject('同步Promise:reject')
setTimeout(() => {
// resolve('异步Promise:resolve');
reject('异步Promise:reject')
}, 100)
})
const res_4_test = new Promise(function(resolve, reject) {
// resolve('testPromise:resolve');
// // reject('testPromise:reject')
setTimeout(() => {
resolve('test异步Promise:resolve');
// reject('test异步Promise:reject')
}, 100)
})
res_4.then(val => {
return 'first'
// return res_4_test
}, err => {
return 'first-err'
}).then(val => {
console.log('two', val)
}, err => {
console.log('two', err)
})
链式调用下,如果返回自身,会出现死循环,出现卡死现象,所以我们是不允许这种现象出现的,以下,我们来处理这种情况:
// 处理then 方法
then(onFulfilled, onRejected) {
const promise = setTimeout(() => new MyPromise((resolve, reject) => {
let func = undefined;
switch (this.status) {
case FULFILLED:
func = onFulfilled(this.value);
resolvePromise(promise, func, resolve, reject)
break;
case REJECTED:
func = onRejected(this.reason);
resolvePromise(promise, func, resolve, reject)
break;
default: // pending状态
this.onFulfilled.push(() => {
func = onFulfilled(this.value)
resolvePromise(promise, func, resolve, reject)
})
this.onRejected.push(() => {
func = onRejected(this.reason)
resolvePromise(promise, func, resolve, reject)
})
}
}), 0)
return promise
}
// 处理resolvePromise
function resolvePromise(promise, func, resolve, reject) {
// 识别对象自返回
if (func === promise) {
return reject(
new TypeError('Chaining cycle detected for promise #' )
)
}
// 返回promise
if (func instanceof MyPromise) {
func.then(resolve, reject)
} else {
resolve(func)
}
}
test
const res_5 = new Promise(function(resolve, reject) {
resolve('同步Promise:resolve');
// reject('同步Promise:reject')
// setTimeout(() => {
// // resolve('异步Promise:resolve');
// reject('异步Promise:reject')
// }, 100)
})
const p = res_5.then((res) => {
console.log(res);
return p;
});
setTimeout: 是为了在MyPromise中获取到声明的对象,以便后续的比较
这一步我们将所有可能发生异常的部分try catch起来,健壮代码
class MyPromise {
constructor(excutor) {
try {
excutor(this.resolve, this.reject);
} catch (e) {
this.reject(e)
}
}
...
then(onFulfilled = val => val, onRejected = err => {throw err}) {
const promise = new MyPromise((resolve, reject) => {
let func = undefined;
switch (this.status) {
case FULFILLED:
setTimeout(() => {
try {
func = onFulfilled(this.value);
resolvePromise(promise, func, resolve, reject)
} catch (e) {}
}, 0)
break;
case REJECTED:
setTimeout(() => {
try {
func = onRejected(this.reason);
resolvePromise(promise, func, resolve, reject)
} catch (e) {}
}, 0)
break;
default: // pending状态
this.onFulfilled.push(() => {
setTimeout(() => {
try {
func = onFulfilled(this.value)
resolvePromise(promise, func, resolve, reject)
} catch (e) {}
}, 0)
})
this.onRejected.push(() => {
setTimeout(() => {
try {
func = onRejected(this.reason)
resolvePromise(promise, func, resolve, reject)
} catch (e) {}
}, 0)
})
}
})
return promise
}
}
test
const res_6 = new Promise(function(resolve, reject) {
// resolve(new TypeError('哈哈,你错了'));
setTimeout(() => {
resolve(new TypeError('哈哈,异步你也错了'));
}, 100)
})
res_6.then((res) => {
console.log(res);
});
static all (array){
let res = [];
return new MyPromise((resolve, reject) => {
const addData = (key, val) => {
res[key] = val;
if (res.length === array.length) {
resolve(res)
}
}
for (let i = 0, len = array.length; i < len; i++) {
let cur = array[i];
if (cur instanceof MyPromise) {
cur.then((val) => addData(i, val), err => reject(err))
} else {
addData(i, cur)
}
}
})
}
test
const test_1 = new Promise((resolve) => {resolve('test_1')})
const test_2 = new Promise((resolve, reject) => {resolve('test_2')})
const test_3 = new Promise((resolve, reject) => {setTimeout(() => {resolve('test_3')}, 100)})
const test_4 = new Promise((resolve, reject) => {setTimeout(() => {reject('test_4')}, 100)})
Promise.all(['1', test_1, test_2, test_3, '2', test_4]).then(res => {
console.log("all",res)
}, err => {
console.log('all-err', err)
})
static resolve(val) {
if (val instanceof MyPromise) return val;
return new MyPromise(resolve => resolve(val))
}
test
Promise.resolve('res_8').then((value) => {
console.log(value)
})
catch(onRejected) {
return this.then(undefined, onRejected)
}
finally(cb) {
this.then(val => MyPromise.resolve(cb()).then(() => val), err => MyPromise.resolve(cb()).then(() => {throw err}))
}
test
const res_9 = new Promise(function(resolve, reject) {
resolve(new TypeError('哈哈,你错了'));
setTimeout(() => {
resolve(new TypeError('哈哈,异步你也错了'));
}, 100)
})
res_9.then(val => {
console.log('9:', val)
}).catch(err => {
console.log('9-err', err)
}).finally(() => {
console.log('finally')
})
☞☞ Github完整版Link
/* 手写promise实现 */
/**
* 1. 完成一个最简单的Promise
* 2. 异步Promise
* 3. then 方法多次调用添加多个处理函数
* 4. promise 的链式调用
* 5. 识别对象自返回
* 6. 错误捕获
* 7. promise.all 方法实现
* 8. promise.resolve 方法实现
* 9. finally 方法实现
*/
// 三种状态
const PENDING = 'pending', FULFILLED = 'fulfilled', REJECTED = 'rejected'
class MyPromise {
constructor(excutor) {
try {
excutor(this.resolve, this.reject);
} catch (e) {
this.reject(e)
}
}
status = PENDING; // 当前的状态
value = undefined; // 成功的值
reason = undefined; // 失败的值
onFulfilled = []; // 存储成功后的回调
onRejected = []; // 存储失败后的回调
resolve = (value) => {
// must not transition to any other state
if (this.status !== PENDING) return
this.status = FULFILLED;
this.value = value;
while(this.onFulfilled.length) this.onFulfilled.shift()()
}
reject = (reason) => {
// must not transition to any other state
if (this.status !== PENDING) return
this.status = REJECTED;
this.reason = reason
while(this.onRejected.length) this.onRejected.shift()()
}
then(onFulfilled = val => val, onRejected = err => {throw err}) {
const promise = new MyPromise((resolve, reject) => {
let func = undefined;
switch (this.status) {
case FULFILLED:
setTimeout(() => {
try {
func = onFulfilled(this.value);
resolvePromise(promise, func, resolve, reject)
} catch (e) {
reject(e);
}
}, 0)
break;
case REJECTED:
setTimeout(() => {
try {
func = onRejected(this.reason);
resolvePromise(promise, func, resolve, reject)
} catch (e) {
reject(e);
}
}, 0)
break;
default: // pending状态
this.onFulfilled.push(() => {
setTimeout(() => {
try {
func = onFulfilled(this.value)
resolvePromise(promise, func, resolve, reject)
} catch (e) {
reject(e);
}
}, 0)
})
this.onRejected.push(() => {
setTimeout(() => {
try {
func = onRejected(this.reason)
resolvePromise(promise, func, resolve, reject)
} catch (e) {}
}, 0)
})
}
})
return promise
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
finally(cb) {
this.then(val => MyPromise.resolve(cb()).then(() => val), err => MyPromise.resolve(cb()).then(() => {throw err}))
}
static all (array) {
let res = [];
return new MyPromise((resolve, reject) => {
const addData = (key, val) => {
res[key] = val;
if (res.length === array.length) {
resolve(res)
}
}
for (let i = 0, len = array.length; i < len; i++) {
let cur = array[i];
if (cur instanceof MyPromise) {
cur.then((val) => addData(i, val), err => reject(err))
} else {
addData(i, cur)
}
}
})
}
static resolve(val) {
if (val instanceof MyPromise) return val;
return new MyPromise(resolve => resolve(val))
}
}
function resolvePromise(promise, func, resolve, reject) {
// 识别对象自返回
if (func === promise) {
return reject(
new TypeError('Chaining cycle detected for promise #' )
)
}
// 返回promise
if (func instanceof MyPromise) {
func.then(resolve, reject)
} else {
resolve(func)
}
}
module.exports = MyPromise