promise规范及应用
课程目标
- promiseA+规范
- 举例说明
知识要点
- promiseA+术语
- promiseA+规范
参考
promiseA+ API 链接
术语
- “
promise”:是一个有then方法的对象或者是函数,行为遵循本规范。 - “
thenable”:是定义then方法的对象或者是函数。 - “
value":是promise状态成功时的值,也就是resolve的参数,包括各种数据类型,也包括undefined/thenable或者是promise。 - “
exception”:是一个使用throw抛出的异常值。 - “
reason”:是promise状态失败时的值,也就是reject的参数,表示拒绝的原因。
规范
promise应该有三种状态:pending、fulfilled、rejected。
pending
- 初始化状态,可改变
- 一个
promise在resolve和reject前都处于这个状态 - 可以能过
resolve-->fulfilled状态 - 可以能过
reject-->rejected状态
fulfilled
- 最终态,不可变
promise被resolve后会变成这个状态- 必须拥有一个
value值,promiser成功后返回的值
rejected
- 最终态,不可变
promise被reject后变成这个状态- 必须拥有
reason,promiser失败后必须有一个错误的理由 ps:“不可变”意味着不变的身份,(即===),并不意味着深刻的不变性
promise的状态流转:
pending --> resolve(value) --> fulfilledpedning --> reject(reason) --> rejected
then
promise必须提供一个then方法来访问当前或最终的值(value)或原因(reason)。
then接收两个参数:onFulfilled、onRejected。
promise.then(onFulfilled,onRejected)
onFulfilled & onRejected
onFulfilled、onReject两个参数如果不是函数,则会忽略。promise状态变成fulfilled或rejected前,onFulfilled或onReject不能调用且不能被多次调用onFulfilled的参数是value,onRejected的参数是reasononFulfilled和onRejected应该是微任务 这里用queueMicrotask来实现微任务的调用
then可以被调用多次
promise状态变成fulfilled后,所有的onFulfilled回调都需要按照then的顺序执行,也就是按照注册顺序执行(在实现的时候需要一个数组来存放多个onFulfilled的回调)promise状态变成rejected后,所有的onRejected回调都需要按照then的顺序执行,也就是按照注册顺序执行(在实现的时候需要一个数组来存放多个onFulfilled的回调)
then返回值
then应该返回一个Promise,是一个新的promise
promise2 = promise1.then(onFulfilled,onRejected)
onFulfilled或onRejected执行的结果为x,调用resolvePromise- 如果
onFulfilled或onRejected执行时抛出异常,promise2需要被rejected。原因就是reason。 - 如果
onFulfiiled不是一个函数并且promise1为fulfilled,promise2会以promise1的value触发fulfilled - 如果
onRejected不是一个函数并且promise1为rejected,promise2会以promise1的reason触发rejected
resolvePromise
resolvePromiseFn(promise2,x,resolve,reject)
- 如果
promise2===x,那么reject TypeError - 如果
x是一个promise:
x是pending状态,那么promise必须保持pending状态,直到x变成fulfilled或rejectedx被fulfilled,promise以相同的value返回x被rejected,promise以相同的reason的理由拒绝
- 如果
x是一个function或object
-
let then = x.then - 如果
x.then出错,那么reject promise with e as the reason - 如果
then是一个函数,then.call(x,resolovePromise,rejectPromise),因为是函数的原因,当前的then指向不是当前的上下文,要把当前函数的this指向当前上下文。相当于then.call(x) => x.then
- 如果
resolvePromise的入参是y,执行resolvePromise(promise2,y,resolve,reject) - 如果
resolvePromise的入参是r,reject promise with r - 如果
resolvePromise和resolvePromise或者多次调用同一个参数,那么第一个调用优先,后面的调用忽略。 - 如果调用
then抛出异常e,如果resolvePromise或rejectPromise已被调用,就忽略。否则reject promise with e as the reason - 如果
then不是一个Function,fulfill promse with x
根据规范手写一个promise
- 定义一个promise类:
class DPromise {
constructor() {
}
}
- promise有三种状态,先定义一下:
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
- 初始化状态以及构造函数的的入参fn,构造函数传参fn要处理一下this指向,fn指向有可能不是当前实例。并且有任何错误的话都要reject。resolve和reject两个方法是要更改status的,从pending改成fulfilled | rejected,两个方法的入参分别是value和reason。
constructor(fn) {
// 初始化状态
this.status = PENDING;
this.value = null;
this.reason = null;
// 同步任务,new promise时会执行
try {
fn(this.resolve.bind(this),this.reject.bind(this));
} catch (e){
this.reject(e);
}
}
resolve(value){
if (this.status === PENDING){
this.status = FULFILLED;
this.value= value;
}
}
rejet(reason){
if (this.status === PENDING){
this.status = REJECTED;
this.reason = reason;
}
}
- then方法会有两个参数onFulfilled,onRejected,两个参数必须是函数,首先要判断一下是不是函数,如果不是要处理一下,这里是手写一个方法isFunction判断。
then(onFulfilled, onRejected) {
// 如果是一个函数就正常返回,不是一个函数给一个默认值
const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {return value;}
const realOnReject = this.isFunction(onrejected) ? onRejected : (reason) => {throw reason}
// then返回一个promise,根据不同的promise状态调用不同的函数
const promise2 = new Promise((resolve,reject) => {
switch(this.status) {
case 'FULFILLED': {
realOnFulfilled();
break;
}
case 'REJECTED': {
realOnReject();
break;
}
}
});
return promise2;
}
isFunction(param) {
return typeof param === 'function'
}
基本上就算写完,可以测试一下
new Promise((resolve,reject) => {
console.info(1);
resove(1)
}).then(value => {
})
在构造函数里执行resolve(1)之后呢,status的状态变成fulfilled,是没问题的。因为是同步执行。如果改成异步呢?
new DPromise((resolve,reject) => {
console.info(1);
setTimeout(() =>{
resolve();
},1000)
}).then(value => {
})
在1s之后再运行,status状态还没变成fulfilled或rejected,有可能还在pending状态,所以需要一个状态的监听机制,当状态变成fulfilled或者rejected后,再执行callballck。
// 数组接收promise值
FULFILLED_CALLBACK_TASK=[]
REJECTED_CALLBACK_TASK=[]
_status = PENDING
// ES6 有提供getter和setter,当status改变时,去做什么事情
// 为什么非要需要一个私有变量?因为会陷入死循环
// 解决异步问题需要一个状态的监听机制,当状态变成fulfilled或者rejected后,再执行callballck。
// 为什么需要get和set 尽量每个方法做到单一处理,一个方法只做一件事。
get status() {
return this._status;
}
set status(newStatus) {
this._status = newStatus;
switch (newStatus) {
case FULFILLED: {
this.FULFILLED_CALLBACK_TASK.forEach(callback => {
callback(this.value)
})
break;
}
case REJECTED: {
this.REJECTED_CALLBACK_TASK.forEach(callback => {
callback(this.reason)
})
break;
}
}
}
resolvePromise代码
resolvePromise(promise2,x,resolve,reject){
// 如果promise2 === x就报错
if(promise2 === x){
return reject(new TypeError('the promise an the return value are the same'))
}
if(x instanceof DPromise){
// 如果 x 为promise,继续执行 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也会被判断为对象,直接resolve
return resolve(x);
}
let then = null;
try {
// 把x.then 赋值给then
then = x.then;
}catch(error){
// 如果获取x.then的值抛出错误error,则以error为原因拒绝promise
return reject(error)
}
// 如果then是一个函数
if(this.isFunction(then)){
// then不需要调用多次
// 需要一个变量called来保证只调用一次
let called = false;
try {
// 将 x 作为函数的作用域 this 调用
then.call(
x,
// 如果resolvePromise以值 y 为函数被调用,则运行resolvePromise
(y) => {
if(called){
return;
}
called = true;
this.resolvePromise(promise2, y, resolve, reject);
},
(r) => {
if(called){
return;
}
called = true;
reject(r);
}
)
}catch(error){
reject(error)
}
}else{
// 如果x不是函数或对象,以x为参数执行promise
resolve(x)
}
} else {
resolve(x);
}
}
完整代码
代码内容还需多复习,规范也要熟知,有一些规范直接放在代码注释上了。
// 手写promise
// 2.定义三种状态类型
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
// 1.初始化class
class DPromise {
// 数组接收promise值
FULFILLED_CALLBACK_TASK=[]
REJECTED_CALLBACK_TASK=[]
_status = PENDING
// 5.Promise构造函数的入参,new promise的时候,就要执行构造函数,并且有任何错误都要被reject出去,比如:new Promise((resove,reject) => {})
//
/**
* 常见面试题 问哪些是同步任务哪些是异步任务
* console.info('begin');
* new Promise((resove,reject) =>{
* console.info(1);
* }).then(() =>{
* console.info(2);
* })
*
* begin和1是同步任务,promise的构造函数是同步任务
* 2是异步任务
*
*
*/
constructor(fn) {
// 3.初始化状态
this.status = PENDING;
this.value = null;
this.reason = null;
// 同步任务,new promise时会执行
try {
fn(this.resolve.bind(this),this.reject.bind(this));
} catch (e){
this.reject(e);
}
}
// ES6 有提供getter和setter,当status改变时,去做什么事情
// 为什么非要需要一个私有变量?因为会陷入死循环
// 解决异步问题需要一个状态的监听机制,当状态变成fulfilled或者rejected后,再执行callballck。
//为什么需要get和set 尽量每个方法做到单一处理,一个方法只做一件事。
get status() {
return this._status;
}
set status(newStatus) {
this._status = newStatus;
switch (newStatus) {
case FULFILLED: {
this.FULFILLED_CALLBACK_TASK.forEach(callback => {
callback(this.value)
})
break;
}
case REJECTED: {
this.REJECTED_CALLBACK_TASK.forEach(callback => {
callback(this.reason)
})
break;
}
}
}
// 4.resolve / reject
resolve(value){
if (this.status === PENDING){
this.status = FULFILLED;
this.value= value;
}
}
rejet(reason){
if (this.status === PENDING){
this.status = REJECTED;
this.reason = reason;
}
}
// 6. then
then(onFulfilled, onRejected) {
// onFulfilled和onRejected是微任务,可以用queueMicrotask包裹执行函数
queueMicrotask(() => {
const fulfilledMicrotask= () => {
try {
// 如果onFulfilled或者onReject返回一个值x,则运行resolvePromise方法
const x = realOnFulfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject);
}catch(e) {
// 如果onFulfilled或onReject抛出一个异常e ,则promise2必须拒绝执行,并返回拒绝原因e,手动catch
reject(e)
}
}
})
queueMicrotask(() => {
const rejectedMicrotask= () => {
try {
// 如果onFulfilled或者onReject返回一个值x,则运行resolvePromise方法
const x = realOnFulfilled(this.value)
this.resolvePromise(promise2, x, resolve, reject)
}catch(e) {
// 如果onFulfilled或onReject抛出一个异常e ,则promise2必须拒绝执行,并返回拒绝原因e,手动catch
reject(e)
}
}
});
// 如果onRejected不是函数且proimse1被拒绝执行,promise2必须拒绝执行并返回相同的原因。
const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {return value;}
const realOnReject = this.isFunction(onrejected) ? onRejected : (reason) => {throw reason}
// then返回一个promise
const promise2 = new Promise((resolve,reject) => {
switch(this.status) {
case 'PENDING':{
this.FULFILLED_CALLBACK_REST.push(fulfilledMicrotask);
this.REJECTED_CALLBACK_REST.push(rejectedMicrotask);
}
case 'FULFILLED': {
fulfilledMicrotask();
break;
}
case 'REJECTED': {
rejectedMicrotask();
break;
}
}
});
return promise2;
}
resolvePromise(promise2,x,resolve,reject){
// 如果promise2 === x 就报错
if(promise2 === x){
return reject(new TypeError('the promise an the return value are the same'))
}
if(x instanceof DPromise){
// 如果 x 为promise,继续执行 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也会被判断为对象,直接resolve
return resolve(x);
}
let then = null;
try {
// 把x.then 赋值给then
then = x.then;
}catch(error){
// 如果获取x.then的值抛出错误error,则以error为原因拒绝promise
return reject(error)
}
// 如果then是一个函数
if(this.isFunction(then)){
// then不需要调用多次
// 需要一个变量called来保证只调用一次
let called = false;
try {
// 将 x 作为函数的作用域 this 调用
then.call(
x,
// 如果resolvePromise以值 y 为函数被调用,则运行resolvePromise
(y) => {
if(called){
return;
}
called = true;
this.resolvePromise(promise2, y, resolve, reject);
},
(r) => {
if(called){
return;
}
called = true;
reject(r);
}
)
}catch(error){
reject(error)
}
}else{
// 如果x不是函数或对象,以x为参数执行promise
resolve(x)
}
} else {
resolve(x);
}
}
isFunction(param) {
return typeof param === 'function'
}
}
手写一个promise.all
promise.all是直接挂载在promise方法上。切记切记
//all方法传参是一个数组
Promise.all = (arr) => {
return new Promise((resolve,reject) => {
//记录参数数组执行结果
let res = [];
// 记录参数数组是否结束
let count = 0;
for (let i = 0; i < arr.length; i++) {
// 防止数组里并不是promise对象,有可能是function或object等其它值,强制转换为promise
Promise.resolve(arr[i])
.then((value) => {
// 存放执行结果,不能用push,all方法
res[i] = value;
// 返回结果跟参数一一对应
count++;
/**
*
* county计算器判断参数数组是否执行结束,不能用res的长度跟arr的长度做对比,
* 因为for是同步,promise是异步,如果第一个promise还没执行完,
* 第二个promise比第一个promise执行速度快(后面的promiser执行速度比前面快),
* res[i]的长度就有问题,会认为promise已经结束,但其实并没有。
* if判断只能放在这里,for是同步,promise是异步,如果放到promise方法外,结果就是undefined
*
*/
if(count === arr.length){
resolve(res);
}
}).catch((reson) =>{
reject(reson);
})
}
})
}
手写promise.finally
是挂载在原型对象上的一个方法。切记切记
Promise.prototype.finally = function (callback) {
// 返回结果对象来者不拒
return this.then((value) => {
return Promise.resolve(callback()).then(() => {value})
},
(err) => {
return Promise.reject(callback()).then(() => {throw err})
})
}
ps:记录一下笔记,方便自己查阅,有问题或不对的地方请各位大佬积极指出,谢谢。