Promise
指标
- Promise A+ 标准
- 手写 Promise
- 题目练习
Promise A+ 标准
术语
- promise: 一个领有合乎这个标准的行为的 then 办法的对象或函数。
- thenable: 定义了一个 then 办法的对象或函数。
- value: 任意非法的 JavaScript 值(包含 undefined,thenable,promise)。
- exception: 应用 throw 语句抛出的一个值
- reason: 示意 promise 为什么被回绝的一个值
必要条件
Promise 状态
promise 必须是这三个状态中的一种:期待态 pending, 解决态 fulfilled 或回绝态 rejected
- 当 promise 处于
pending
状态的时候:
可能变为fulfilled
或者rejected
状态。 - 当 promise 处于
fulfilled
状态的时候:
2.1 肯定不能转换为任何其它状态
2.2 必须有一个不能扭转的value
- 当 promise 处于
rejected
状态的时候:
3.1 肯定不能转换为任何其它状态
3.2 必须有一个不能扭转的reason
在这里,” 肯定不能扭转 ” 意味着不变的身份(例如 ===),然而并不意味着深度不可变性。(译注者:这里应该是说只有值的援用雷同即可,并不需要援用中的每一个值都相等)
then 办法
Promise
必须提供一个 then
办法来拜访以后或最终的 value
或reason
。
Promise 的 then 办法承受俩个参数:
promise.then(onFulfilled, onRejected)
onFulfilled
和onRejected
都是可选的参数
1.1. 如果onFulfilled
不是一个函数,它必须被疏忽
1.2. 如果onRejected
不是一个函数,它必须被疏忽- 如果
onFulfilled
是一个函数
2.1. 在 promise 变成fulfilled
时,应该调用 onFulfilled, 参数是value
2.2. 在 promise 变成fulfilled
之前, 不应该被调用.
2.3. 它肯定不能被调用屡次。 - 如果
onRejected
是一个函数
2.1. 在 promise 变成rejected
时,应该调用 onRejected, 参数是reason
2.2. 在 promise 变成rejected
之前, 不应该被调用.
2.3. 它肯定不能被调用屡次。 - 在执行上下文栈中只蕴含平台代码之前,onFulfilled 或 onRejected 肯定不能被调用
- 同一个 promise 上的 then 可能被调用屡次
6.1. 如果 promise 被解决,所有相应的 onFulfilled 回调必须依照他们原始调用 then 的程序执行
6.2. 如果 promise 被回绝,所有相应的 onRejected 回调必须依照他们原始调用 then 的程序执行 - then 必须返回一个 promise 对象
promise2 = promise1.then(onFulfilled,onRejected)
resolvePromise
resolvePromise(promise2, x, resolve, reject)
- 如果 promise 和 x 援用同一个对象,用一个 TypeError 作为起因来回绝 promise
- 如果 x 是一个 promise, 依据
x
的状态:
2.1. 如果 x 是pending
,promise 必须放弃期待状态,直到 x 被解决或回绝
2.2. 如果 x 是fulfilled
,用雷同的value
解决 promise
2.3. 如果 x 是rejected
,用雷同的reason
回绝 promise -
否则,如果 x 是一个对象或函数
3.1. 让 then 成为 x.then
let then = x.then.
3.2. 如果检索属性 x.then 导致抛出了一个异样 e,用 e 作为起因回绝 promise
3.3. 如果 then 是一个函数,用 x 作为 this 调用它。then 办法的参数为俩个回调函数,第一个参数叫做 resolvePromise,第二个参数叫做 rejectPromise:3.3.1. 如果 resolvePromise 用一个值 y 调用,运行[[Resolve]](promise, y)。译者注:这里再次调用[[Resolve]](promise,y),因为 y 可能还是 promise 3.3.2. 如果 rejectPromise 用一个起因 r 调用,用 r 回绝 promise。译者注:这里如果 r 为 promise 的话,依旧会间接 reject,回绝的起因就是 promise。并不会等到 promise 被解决或回绝 3.3.3. 如果 `resolvePromise` 和 `rejectPromise` 都被调用,或者对同一个参数进行屡次调用,那么第一次调用优先,当前的调用都会被疏忽。译者注:这里次要针对 thenable,promise 的状态一旦更改就不会再扭转。3.3.4. 如果调用 then 抛出了一个异样 e, 3.3.4.1 如果 `resolvePromise` 或 `rejectPromise` 曾经被调用,疏忽它 3.3.4.2 否则,用 `e` 作为起因回绝 promise
3.4. 如果 then 不是一个函数,用 x 解决 promise
手写 Promise
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
/**
* 1. 构建 Promsie 类
* 2. 定义 3 种状态类型
* 3. 设置初始状态
* 4. resolve 和 reject 办法
* 5. 定义结构参数
* 5.1 入参是一个函数, 函数接管 resolve 和 reject 两个参数.
* 5.2 留神在初始化 promise 的时候, 就要执行这个函数, 并且有任何报错都要通过 reject 抛出去
* 6. 实现 then 办法
*/
class MPromise {FULFILLED_CALLBACK_LIST = [];
REJECTED_CALLBACK_LIST = [];
_status = PENDING;
constructor(fn) {
// this.status = PENDING;
this.value = null;
this.reason = null;
try {fn(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {this.reject(e.message);
}
}
get status() {return this._status;}
set status(newStatus) {
this._status = newStatus;
switch (newStatus) {
case FULFILLED: {this.FULFILLED_CALLBACK_LIST.forEach((callback) => {callback(this.value);
});
break;
}
case REJECTED: {this.REJECTED_CALLBACK_LIST.forEach((callback) => {callback(this.reason);
});
break;
}
}
}
resolve(value) {if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
}
}
reject(reason) {if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
}
}
then(onFulfilled, onRejected) {const realOnFulfilled = this.isFunction(onFulfilled)
? onFulfilled
: (value) => value;
const realOnRejected = this.isFunction(onRejected)
? onRejected
: (reason) => {throw reason;};
const promise2 = new MPromise((resolve, reject) => {const fulfilledMicrotask = () => {queueMicrotask(() => {
try {const x = realOnFulfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {reject(e);
}
});
};
const rejectedMicrotask = () => {queueMicrotask(() => {
try {const x = realOnRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {reject(e);
}
});
};
switch (this.status) {
case FULFILLED: {fulfilledMicrotask();
break;
}
case REJECTED: {rejectedMicrotask();
break;
}
case PENDING: {this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask);
this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask);
}
}
});
return promise2;
}
resolvePromise(promise2, x, resolve, reject) {
// 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise
if (promise2 === x) {
return reject(new TypeError("The promise and the return value are the same")
);
}
if (x instanceof MPromise) {
// 如果 x 为 Promise,则使 newPromise 承受 x 的状态
// 也就是继续执行 x,如果执行的时候拿到一个 y,还要持续解析 y
queueMicrotask(() => {x.then((y) => {this.resolvePromise(promise2, y, resolve, reject);
}, reject);
});
} else if (typeof x === "object" || this.isFunction(x)) {
// 如果 x 为对象或者函数
if (x === null) {
// null 也会被判断为对象
return resolve(x);
}
let then = null;
try {
// 把 x.then 赋值给 then
then = x.then;
} catch (error) {
// 如果取 x.then 的值时抛出谬误 e,则以 e 为据因回绝 promise
return reject(error);
}
// 如果 then 是函数
if (this.isFunction(then)) {
let called = false;
try {
then.call(
x,
// 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise
(y) => {
// 须要有一个变量 called 来保障只调用一次.
if (called) return;
called = true;
this.resolvePromise(promise2, y, resolve, reject);
},
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 回绝 promise
(r) => {if (called) return;
called = true;
reject(r);
}
);
} catch (error) {
// 如果调用 then 办法抛出了异样 e:if (called) return;
// 否则以 e 为据因回绝 promise
reject(error);
}
} else {resolve(x);
}
} else {resolve(x);
}
}
static resolve(value) {if (value instanceof MPromise) {return value;}
return new MPromise((resolve) => {resolve(value);
});
}
static reject(reason) {return new MPromise((resolve, reject) => {reject(reason);
});
}
static race(promiseList) {return new MPromise((resolve, reject) => {
const length = promiseList.length;
if (length === 0) {return resolve();
} else {for (let i = 0; i < length; i++) {MPromise.resolve(promiseList[i]).then((value) => {return resolve(value);
},
(reason) => {return reject(reason);
}
);
}
}
});
}
static all(promiseList) {let results = [];
let promiseCount = 0;
let promisesLength = promiseList.length;
return new MPromise((resolve) => {Promise.resolve(val).then((res) => {
promiseCount++;
results[i] = res;
if (promiseCount === promisesLength) {return resolve(results);
}
},
(err) => {return reject(err);
}
);
});
}
isFunction(param) {return typeof param === "function";}
}
练习
- 求运行后果
const promise = new Promise((resolve, reject) => {console.log(1)
resolve()
console.log(2) // 这里有纳闷
})
promise.then(() => {console.log(3)
})
console.log(4)
运行后果:
1
2
4
3
- 求运行后果
const promise1 = new Promise((resolve, reject) => {setTimeout(() => {resolve('success')
}, 1000)
})
const promise2 = promise1.then(() => {throw new Error('error!!!') // 这里不太确定 status
})
console.log('promise1', promise1)
console.log('promise2', promise2)
setTimeout(() => {console.log('promise1', promise1)
console.log('promise2', promise2)
}, 2000)
运行后果
promise1 Promise {<pending>}
promise2 Promise {<pending>}
(node:50928) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: error!!!
(node:50928) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
promise1 Promise {'success'}
promise2 Promise {
<rejected> Error: error!!!
at promise.then (...)
at <anonymous> }
- 求运行后果
const promise = new Promise((resolve, reject) => {resolve('success1')
reject('error')
resolve('success2')
})
promise
.then((res) => {console.log('then:', res)
})
.catch((err) => {console.log('catch:', err)
})
运行后果
then: success1
- 求运行后果
Promise.resolve(1)
.then((res) => {console.log(res)
return 2
})
.catch((err) => {return 3})
.then((res) => {console.log(res)
})
运行后果
1
2
catch 如果胜利执行,会返回雷同的 value 也就是 2.
- 求运行后果
const promise = new Promise((resolve, reject) => {setTimeout(() => {console.log('once')
resolve('success')
}, 1000)
})
const start = Date.now()
promise.then((res) => {console.log(res, Date.now() - start)
})
promise.then((res) => {console.log(res, Date.now() - start)
})
运行后果
once
success 1009
success 1009
- 求运行后果
Promise.resolve()
.then(() => {return new Error('error!!!')
})
.then((res) => {console.log('then:', res)
})
.catch((err) => {console.log('catch:', err)
})
运行后果
then: Error: error!!!
at Promise.resolve.then (...)
at ...
then 的 onFulfilled 函数外面批改 rejected 的:
return Promise.reject(new Error('error!!!'))
throw new Error('error!!!')
- 求运行后果
const promise = Promise.resolve()
.then(() => {return promise})
promise.catch(console.error)
运行后果
TypeError: Chaining cycle detected for promise #<Promise>
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
at Function.Module.runMain (module.js:667:11)
at startup (bootstrap_node.js:187:16)
at bootstrap_node.js:607:3
- 求运行后果
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
运行后果
1
解释:then 函数的 onFulfilled 不为函数,默认为(value)=>value
- 求运行后果
Promise.resolve()
.then(function success (res) {throw new Error('error')
}, function fail1 (e) {console.error('fail1:', e)
})
.catch(function fail2 (e) {console.error('fail2:', e)
})
运行后果
fail2: Error: error
at success (...)
at ...
解释:resolve
函数执行完返回 fullfile
状态的 promsie 对象。在第一个 then
函数的 onFullfield
抛出异样。则返回 promsie 对象的状态为 rejected
。被catch
函数捕捉。
- 求运行后果
process.nextTick(() => {console.log('nextTick')
})
Promise.resolve()
.then(() => {console.log('then')
})
setImmediate(() => {console.log('setImmediate')
})
console.log('end')
end
nextTick
then
setImmediate
解释:process.nextTick
和 promise.then
都属于 microtask,而 setImmediate
属于 macrotask,在事件循环的 check 阶段执行。事件循环的每个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。
小结
promise 的实现最难了解的就是 resolvePromise
,十分的绕。另外学习到的了一个新的 APIqueueMicrotask
, 有些文章是应用setTimeout
来模仿实现微工作调用。
setTimeout(() => {},0)
参考文章
- Promises/A+
- Promise/A+ 标准(译)
- Promise.all 手动实现