promise 语法
只有promise对象后面可以接.then,如下所示,当执行A函数时,会立即返回一个 promise 对象,只不过此时 promise 的状态为 pending,此时then不会执行,一秒后promise状态变为reslove后,then里面的回调才会执行。
js
let count = 1
function A() {
return new Promise((resolve, reject) => {
setTimeout(() => {
count = 100
resolve('ok') // 将 promise 的状态改为 resolve
}, 1000)
})
}
function B() {
console.log(count)
}
// 0ms后 A 就返回了一个 promise 对象,但是这个promise对象的状态为 pending
// 1000ms后 A 的 promise 对象状态变为 resolved
// then 中的回调函数 B 才会执行
A().then((res) => {
B()
console.log(res); // 输出 ok
})
由此可得,return一个promise对象是立即执行的,then里面的回调只有在该promise状态变更才会执行,知道了以上原理,再比较一下下面三种写法:
js
let count = 1
function A() {
return new Promise((resolve, reject) => {
setTimeout(() => {
count = 100
resolve()
}, 1000)
})
}
function B() {
return new Promise((resolve, reject) => {
setTimeout(() => {
count = 200
resolve()
}, 500)
})
}
function C() {
console.log(count);
}
js
// 第一种:
A().then(() => {
B().then(() => {
C()
})
})
每个.then()
回调函数的执行是基于前一个Promise的状态,第二个then只有当执行完B函数返回reslove才会改变promise的状态变为reslove,然后才执行 .then 里面的回调。在这里,C()
函数的执行依赖于B()
函数返回的Promise被解决。所以输出 200.
js
// 第二种
A()
.then(() => {
B()
})
.then(() => {
C()
})
第二个 .then 是接在第一个.then后面的,而不是B函数后面,每个then的返回值是一个新的promise对象,第二个then接收的是第一个then的返回值,当1s后执行完A函数时,第一个then状态变为reslove,而此时第二个then接收第一个then返回的的promise状态reslove,所以此时B跟C函数一起执行,C函数不耗时,所以直接打印100,0.5s后B执行将count改为200,但是此时已经打印完了,所以最终结果 100.
js
// 第三种
A()
.then(() => {
return B()
})
.then(() => {
C()
})
这时候第一个 .then 回调里面有 return,这时候第二个then就会以第一个then回调里面return出的为上一个的promise状态,所以输出 200.
总的来说:then 方法的返回值是一个新的 promise,状态为 pending,then 的 pending 状态会根据上一个 promise 状态的修改而修改。
promise 源代码
要想打造一个promise就需要在MyPromise类里面添加以一个constructor构造器,参数为传进来的回调函数,因为Promise是return new Promise((resolve, reject) => {}
这样使用的。所以需要传入两个函数体作为executor的参数,且里面是可以resolve出来值的,reject出来的值给catch回调函数调用,所以再给两个函数补个形参,如下;
js
class MyPromise {
constructor(executor) { // 传进来的函数体
this.value = null // 成功的结果
this.reject = null // 失败的原因
const reslove = (value) => {
this.value = value
}
const reject = () => {
this.reject = reject
}
executor(reslove, reslove) // 参数为两个函数体 reslove reject
}
}
而且promise状态一经变更就不会再变回去了,比如先reslove再reject,就只会变更为reslove状态,而不会再变更为reject状态,但是会触发reject而不会变更状态。这个实现就是添加一个初始状态this.status === pending
,当reslove跟reject函数里面更改状态时,先判断当前状态是否为pending,是这个状态则表示状态还未确定,然后改变状态,这样就可以得到会执行但是不会变更状态的效果了。
js
class MyPromise {
constructor(executor) { // 传进来的函数体
this.status = 'pending' // 初始状态为 pending,表示 promise 对象的状态还未被确定
this.value = null // 成功的结果
this.reject = null // 失败的原因
const reslove = (value) => {
this.value = value
if (this.status === 'pending') {
this.status = 'fulfilled' // 状态变为 fulfilled
}
}
const reject = () => {
this.reject = reject
if (this.status === 'pending') {
this.status = 'rejected' // 状态变为 rejected
}
}
executor(reslove, reslove) // 参数为两个函数体 reslove reject
}
}
再注意一个细节,then后面的函数是根据promise状态来触发的,当调用reslove或reject更改状态时才触发,于是直接将then后面的回调函数放到这reslove或reject函数体里面触发即可,同时传入的参数刚好是前面reslove或reject的值。
需要用一个数组来存放回调函数,因为如果是then接在then后面,且前面的then里面没有return,如下;
js
A()
.then(() => {
B()
})
.then(() => {
C()
})
则A函数返回的promise状态的改变会同时触发函数B和C。
所以最后的promise源码:
js
class MyPromise {
constructor(executor) { // 传进来的函数体
this.status = 'pending' // 初始状态为 pending,表示 promise 对象的状态还未被确定
this.value = null // 成功的结果
this.reason = null // 失败的原因
this.onFulfilledCallbacks = [] // 存放成功的回调函数
this.onRejectedCallbacks = [] // 存放失败的回调函数
const reslove = (value) => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled' // 状态变为 fulfilled
this.onFulfilledCallbacks.forEach(fn => fn(value))
}
}
const reject = (reason) => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected' // 状态变为 rejected
this.onRejectedCallbacks.forEach(fn => fn(reason))
}
}
executor(reslove, reslove) // 参数为两个函数体 reslove reject
}
}
总的来说,就是定义两个函数(函数体里面存形参)作为实参传给executor,然后记住状态改变,以及触发函数。
then 源代码
至于then里面回调函数是如何放入这个数组中的是then函数干的事了。then函数是挂在promise对象的原型上。所以直接在MyPromise类中定义一个then函数即可。这个函数就挂在原型上。
首先then后面是可以接收两个回调的。第二个回调函数完全等同于.catch的功能。
js
A()
.then(
(res) => {
B()
},
(err) => {
console.log(err); // reject出来的值
}
)
then 原理:
- 接收两个参数,第一个是成功的回调,第二个是失败的回调
- 返回一个新的promise
- 当then执行到的时刻,then前面的promise状态已经变更为fulfilled或rejected,then中的回调函数由then自己执行
- 当then执行到的时刻,then前面的状态变为pending,then中的回调函数会先被存放,等待promise状态变更为fulfilled或rejected时再由resolve或reject函数执行
第三点就发生在当前面的promise对象不耗时的时候,promise里面放同步代码的时候,里面的回调就有then函数自己触发,就不需要由promise对象里面的resolve函数触发,如下;
js
function A() {
return new MyPromise((resolve, reject) => {
resolve('ok')
})
}
function B() {
console.log('b');
}
A.then(
B()
)
然后then的完整源代码如下:
js
class MyPromise {
// constructor(executor) { ... }
then(onFulfilled, onRejected) {
// 判断传入的两个参数是否为 函数体
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value // value => value 是一个函数体
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
// 返回一个新的promise对象
let newPromise = new MyPromise((resolve, reject) => {
if (this.status === 'fulfilled') { // this 是 then 的
setTimeout(() => {
try {
// .then是异步微任务,由于很难打造成微任务,于是可以用定时器打造成异步任务
const result = onFulfilled(this.value)
// 也就是当回调里面有return 出一个对象是promise对象时返回这个对象
if (result instanceof Promise) {
newPromise = result
}
resolve(result)
} catch (error) {
reject(error)
}
})
}
if (this.status === 'rejected') {
setTimeout(() => {
try {
const result = onRejected(this.reason)
if (result instanceof Promise) {
newPromise = result
}
resolve(result)
} catch (error) {
reject(error)
}
})
}
if (this.status === 'pending') {
this.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
try {
const result = onFulfilled(value)
if (result instanceof Promise) {
newPromise = result
}
resolve(result)
} catch (error) {
reject(error)
}
})
})
// 将函数push进成功的成功的回调函数数组,让Promise里面的reslove调用,
// 也就是当前面的promise返回状态 fulfilled 时调用
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
try {
const result = onRejected(reason)
if (result instanceof Promise) {
newPromise = result
}
resolve(result)
} catch (error) {
reject(error)
}
})
})
}
})
return newPromise
}
catch(onRejected) {
return this.then(null, onRejected);
}
}