promise 学习笔记

72 阅读9分钟

我们都知道promise是处理异步的,在没有promise以前我们都是回调函数,发布订阅来监听异步函数的执行状态,但是很容易就会产生地狱式的回调,所以咱们可以通过promise来优雅的解决异步问题,通过.then的方式更直观的来解决异步问题,当然该有generator的方式来解决, 这里并不记录generator,所以就不做过多的介绍的啦

我们先看一下promise的用法,假如一个函数有很多的回调,Ajax,异步文件读取

fs.readFile(path.resolve(__dirname, "1.xxx"), "utf-8", function (err,data) {
    console.log(data);
    fs.readFile(path.resolve(__dirname, "2.xxx"), "utf-8", function (err,data) {
        console.log(data);
        fs.readFile(path.resolve(__dirname, "3.xxx"), "utf-8", function (err,data) {
            console.log(data);
        });
    });
});

这样读取文件,3个文件就需要这么多回调,那么用咱们promise怎么解决呢?

let p = new Promise((reslove,reject) => {
    fs.readFile(path.resolve(__dirname, '1.xxx'), 'utf-8', function (err, data) {
        reslove( data);
    });
})
p.then(() => {
    fs.readFile(path.resolve(__dirname, '2.xxx'), 'utf-8', function (err, data) {
        reslove( data);
    });
})
p.then(() => {
    fs.readFile(path.resolve(__dirname, '3.xxx'), 'utf-8', function (err, data) {
        reslove( data);
    });
})

这样是不是就直观多了呢,那promise到底是如何实现的呢?我们先来简单分析一下

  1. promise 是一个构造函数,默认需要传入一个executor执行器
  2. executor 会立刻执行,并且传入 resolve 和 reject 两个参数
  3. promise 有三个状态 fulfilled 成功 reject 拒绝态 pending等待态 (默认是等待态)
  4. 每个promise都有一个then方法 , 可以访问到成功的值和失败的原因
  5. 可以通过resolve和reject来改变状态,同时调用对应的回调, 一个promise实例状态变化后,不能再重新的发生变化
  6. 或者当executor 发生异常的时候 也会触发promise的失败

基于以上特点我们尝试手写一个promise, 首先promise肯定是一个类,传入的是一个函数。并且有三种状态,还有resolve和reject,咱们可以根据这些特点写一个简单的,

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
    constructor(executor) {
        const resolve = (value) => {};

        const reject = (reason) => {};
        // 这里方法有可能一上来就执行失败,所以我们可以用 try/catch 捕获一下
        try {
			// 由于new Promise 的时候有resolve,reject这里传递一下
                executor(resolve, reject);
        } catch (error) {
                // 如果失败了直接 reject掉
                reject(error);
        }
   }
}

这样咱们就完成一个基础的架子了,由于resolve和reject都会向下传递失败或者成功的值,咱们我们还要继续保存成功或者失败的值传给then,这里说到then,第四步 每个promise都有一个then方法 , 可以访问到成功的值和失败的原因,所以咱们promise还需要一个then的方法,传入onFulfilled, onRejected,一个then还可以调用多次,还有状态一直pending状态。例如下面

const promise = new Promise((resolve, reject) => {
	setTimeout(() => {
		resolve('ok');
	}, 1000);
});
promise.then(
	(data) => {
		console.log(data, 'success1');
	},
	(reason) => {
		console.log(reason, 'fail');
	}
);
promise.then(
	(data) => {
		console.log(data, 'success2');
	},
	(reason) => {
		console.log(reason, 'fail');
	}
);

因为上面延迟了1秒resolve,咱们的then又是同步的,所以没等到状态改变,then已经执行,所以咱们还需要用数组处理一下多个then和延迟下的状态改变

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
  constructor(executor) {
	  this.status = PENDING; // 初始化状态是 PENDING 状态
	  this.value = undefined;
	  this.reason = undefined;
	  this.onResolvedCallbacks = []; // 成功的回调
	  this.onRejectedCallbacks = []; // 失败的的回调
	  const resolve = (value) => {
		  if (this.status === PENDING) {
			  this.status = FULFILLED;
			  this.value = value;
			  // 成功调用成功的回调
			  this.onResolvedCallbacks.forEach((cb) => cb());
		  }
	  };

	  const reject = (reason) => {
		  if (this.status === PENDING) {
			  this.status = REJECTED;
			  this.reason = reason;
			  // 失败调用失败的回调
			  this.onRejectedCallbacks.forEach((cb) => cb());
		  }
	  };
		// 这里方法有可能一上来就执行失败,所以我们可以用 try/catch 捕获一下
	  try {
		  // 由于new Promise 的时候有resolve,reject这里传递一下
		  executor(resolve, reject);
	  } catch (error) {
		  // 如果失败了直接 reject掉
		  reject(error);
	  }
  }
  then(onFulfilled, onRejected) {
	  if (this.status === FULFILLED) {
		  onFulfilled(this.value);
	  }
	  if (this.status === REJECTED) {
		  onRejected(this.reason);
	  }
	  // 有可能到这里状态依然是 PENDING
	  if (this.status === PENDING) {
		  this.onResolvedCallbacks.push(() => {
			  // 这里可以做其他事
			  onFulfilled(this.value);
		  });
		  this.onRejectedCallbacks.push(() => {
			  onRejected(this.reason);
		  });
	  }
  }
}

写到这里,基本版的promise可以运行了。但是then还有有问题,咱们知道then有成功和失败的回调,如果失败的回调里边返回一个普通值,会走到下一个then的成功里边,这也是咱们熟悉的链式调用,每个then都是返回一个新的promise

image.png 就像这个例子,失败return 100,会走到下一个then的成功,由于还可 throw new Error() 或者reject来改变then的状态,所以咱们包一层try/catch所以咱们还要改一下代码

then(onFulfilled, onRejected) {
 let Promise2 = new Promise((resolve, reject) => {
	if (this.status === FULFILLED) {
		try {
			let x = onFulfilled(this.value);
			resolve(x);
		} catch (error) {
			reject(error);
		}
	}
	if (this.status === REJECTED) {
		try {
			let x = onRejected(this.reason);
			resolve(x);
		} catch (error) {
			reject(error);
		}
	}
	// 有可能到这里状态依然是 PENDING
	if (this.status === PENDING) {
		this.onResolvedCallbacks.push(() => {
			// 这里可以做其他事
			try {
				let x = onFulfilled(this.value);
				resolve(x);
			} catch (error) {
				reject(error);
			}
		});
		this.onRejectedCallbacks.push(() => {
			try {
				let x = onRejected(this.reason);
				resolve(x);
			} catch (error) {
				reject(error);
			}
		});
	}
  });
 return Promise2;
}

最后then的方法变成这样了,咱们还要考虑then里边再返回promise,还要看看返回的是不是自己,所以咱们要封装一个函数 resolvePromise,里边传入Promise2,x,resolve,reject,由于咱们再then调用resolvePromise,promise又是同步代码,所以此时咱们还拿不到Promise2,因此咱们可以在外边包一层setTimeout,来延迟执行。 下面咱们开始写resolvePromise, 具体细节请看代码注释

function resolvePromise(promise2, x, resolve, reject) {
  // 1/ 判断是不是返回的自己
  if (promise2 === x) {
     throw Error('不要循环引用呀');
  }  // 2 如果是对象或者函数才有可能是 promise (注意是有可能)  
  if (typeof x === 'object' || typeof x === 'function') {
  let flag = false;
  // 3 取 then 的时候有可能会发生错误 我们认为有then才是一个promise
  try {
	  let then = x.then;
	  // then方法可能是通过defineProperty来进行定义的
	  if (typeof then === 'function') {
		  // 用第一次取出的 then 来call 下x ,就预防 defineProperty 定义的then多次取值了
		  then.call(
			  x,
			  (y) => {
				  // flag 是预防一个在 promise 里多次调用resolve
				  if (flag) return;
				  flag = true;
				  // 这里是 then 成功的回调  这里的 y 有可能还是一个promise 所以需要递归执行一下
				  resolvePromise(promise2, y, resolve, reject);
			  },
			  (r) => {
				  // flag 是预防一个在 promise 里多次调用reject
				  if (flag) return;
				  flag = true;
				  // 这里是 then 失败的回调
				  reject(r);
			  }
		  );
	  } else {
		  // flag 是预防一个在 promise 里多次调用reject
		  if (flag) return;
		  flag = true;
		  // 就是一个对象或者函数  {}  function(){}
		  resolve(x);
	  }
  } catch (error) {
	  // flag 是预防一个在 promise 里多次调用reject
	  if (flag) return;
	  flag = true;
	  reject(error);
  }  } else {
  // 普通值 直接将结果传递到下面就可以了
  resolve(x);  }
}

下面我再实现几个promise,常用的方法,比如all,race等,首先改在一下咱们的resolve,万一传的值是Promise咋办,咱们要递归解析一下

resolve = (value) => {
    // 新加的代码
  if (value instanceof Promise) {
	  // 递归解析的流程
	  return value.then(resolve, reject);
  }
  if (this.status === PENDING) {
	  this.status = FULFILLED;
	  this.value = value;
	  // 成功调用成功的回调
	  this.onResolvedCallbacks.forEach((cb) => cb());
  }
}

all 是都成功才能成成功,有一个失败就是失败

all(values) {
  return new Promise((resolve, reject) => {
	  let arr = [];
	  let resultIndex = 0;
	  // 这里定义一函数,等计数器 和传进来的数组一致就 resolve
	  function realutData(index, data) {
		  arr[index] = data;
		  if (++resultIndex === values.length) {
			  resolve(arr);
		  }
	  }
          for (let i = 0; i < values.length; i++) {
                    // values[i] 有可能是普通值 所以要包装一下,返回 Promise
                    let result = Promise.resolve(values[i]);
                    result.then((data) => {
                            realutData(i, data);
             }, reject);
	  }
  });
}

race 与all 相反有一个失败就失败, 有一个成功就成功了

race(values) {
  return new Promise((resolve, reject) => {
	  // 只拿第一次的结果,无论成功失败都进不来了
	  for (let i = 0; i < values.length; i++) {
		  Promise.resolve(values[i]).then(resolve, reject);
	  }
});
}

allSettled用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了”fulfilled“和”rejected“两种情况。和all类似,我们可以把all改造一下

allSettled(values) {
    return new Promise((resolve, reject) => {
	    let arr = [];
	    let resultIndex = 0;
	    // 这里定义一函数,等计数器 和传进来的数组一致就 resolve
	    function realutData(status, data) {
		    if (status == 'rejected') {
			    arr.push({
				    status,
				    reason: data
			    });
		    } else {
			    arr.push({
				    status,
				    value: data
			    });
		    }
		    if (++resultIndex === values.length) {
			    resolve(arr);
		    }
	    }
	    for (let i = 0; i < values.length; i++) {
		    // values[i] 有可能是普通值 所以要包装一下,返回 Promise
		    let result = Promise.resolve(values[i]);
		    result.then(
			    (data) => {
				    realutData('fulfilled', data);
			    },
			    (err) => {
				    realutData('rejected', err);
			    }
		    );
	    }
    });
}

附完整代码

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
function resolvePromise(promise2, x, resolve, reject) {
	// 1/ 判断是不是返回的自己
	if (promise2 === x) {
		throw Error('不要循环引用呀');
	}
	// 2 如果是对象或者函数才有可能是 promise (注意是有可能)
	if (typeof x === 'object' || typeof x === 'function') {
		let flag = false;
		// 3 取 then 的时候有可能会发生错误 我们认为有then才是一个promise
		try {
			let then = x.then;
			// then方法可能是通过defineProperty来进行定义的
			if (typeof then === 'function') {
				// 用第一次取出的 then 来call 下x ,就预防 defineProperty 定义的then多次取值了
				then.call(
					x,
					(y) => {
						// flag 是预防一个在 promise 里多次调用resolve
						if (flag) return;
						flag = true;
						// 这里是 then 成功的回调  这里的 y 有可能还是一个promise 所以需要递归执行一下
						resolvePromise(promise2, y, resolve, reject);
					},
					(r) => {
						// flag 是预防一个在 promise 里多次调用reject
						if (flag) return;
						flag = true;
						// 这里是 then 失败的回调
						reject(r);
					}
				);
			} else {
				// flag 是预防一个在 promise 里多次调用reject
				if (flag) return;
				flag = true;
				// 就是一个对象或者函数  {}  function(){}
				resolve(x);
			}
		} catch (error) {
			// flag 是预防一个在 promise 里多次调用reject
			if (flag) return;
			flag = true;
			reject(error);
		}
	} else {
		// 普通值 直接将结果传递到下面就可以了
		resolve(x);
	}
}
class Promise {
	constructor(executor) {
		this.status = PENDING; // 初始化状态是 PENDING 状态
		this.value = undefined;
		this.reason = undefined;
		this.onResolvedCallbacks = []; // 成功的回调
		this.onRejectedCallbacks = []; // 失败的的回调
		const resolve = (value) => {
			if (value instanceof Promise) {
				// 递归解析的流程
				return value.then(resolve, reject);
			}
			if (this.status === PENDING) {
				this.status = FULFILLED;
				this.value = value;
				// 成功调用成功的回调
				this.onResolvedCallbacks.forEach((cb) => cb());
			}
		};

		const reject = (reason) => {
			if (this.status === PENDING) {
				this.status = REJECTED;
				this.reason = reason;
				// 失败调用失败的回调
				this.onRejectedCallbacks.forEach((cb) => cb());
			}
		};
		// 这里方法有可能一上来就执行失败,所以我们可以用 try/catch 捕获一下
		try {
			// 由于new Promise 的时候有resolve,reject这里传递一下
			executor(resolve, reject);
		} catch (error) {
			// 如果失败了直接 reject掉
			reject(error);
		}
	}
	then(onFulfilled, onRejected) {
		onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (v) => v;
		onRejected =
			typeof onRejected === 'function'
				? onFulfilled
				: (e) => {
						throw e;
				  };
		let Promise2 = new Promise((resolve, reject) => {
			if (this.status === FULFILLED) {
				setTimeout(() => {
					try {
						let x = onFulfilled(this.value);
						resolvePromise(Promise2, x, resolve, reject);
					} catch (error) {
						reject(error);
					}
				});
			}
			if (this.status === REJECTED) {
				setTimeout(() => {
					try {
						let x = onRejected(this.reason);
						resolvePromise(Promise2, x, resolve, reject);
					} catch (error) {
						reject(error);
					}
				});
			}
			// 有可能到这里状态依然是 PENDING
			if (this.status === PENDING) {
				this.onResolvedCallbacks.push(() => {
					// 这里可以做其他事
					setTimeout(() => {
						try {
							let x = onFulfilled(this.value);
							resolvePromise(Promise2, x, resolve, reject);
						} catch (error) {
							reject(error);
						}
					});
				});
				this.onRejectedCallbacks.push(() => {
					setTimeout(() => {
						try {
							let x = onRejected(this.reason);
							resolvePromise(Promise2, x, resolve, reject);
						} catch (error) {
							reject(error);
						}
					});
				});
			}
		});
		return Promise2;
	}
	catch(callback) {
		return this.then(null, callback);
	}
	static resolve(value) {
		return new Promise((resolve, reject) => {
			resolve(value);
		});
	}
	static reject(value) {
		return new Promise((resolve, reject) => {
			reject(value);
		});
	}
	static all(values) {
		return new Promise((resolve, reject) => {
			let arr = [];
			let resultIndex = 0;
			// 这里定义一函数,等计数器 和传进来的数组一致就 resolve
			function realutData(index, data) {
				arr[index] = data;
				if (++resultIndex === values.length) {
					resolve(arr);
				}
			}
			for (let i = 0; i < values.length; i++) {
				// values[i] 有可能是普通值 所以要包装一下,返回 Promise
				let result = Promise.resolve(values[i]);
				result.then((data) => {
					realutData(i, data);
				}, reject);
			}
		});
	}
	static race(values) {
		return new Promise((resolve, reject) => {
			// 只拿第一次的结果,无论成功失败都进不来了
			for (let i = 0; i < values.length; i++) {
				Promise.resolve(values[i]).then(resolve, reject);
			}
		});
	}
	static allSettled(values) {
		return new Promise((resolve, reject) => {
			let arr = [];
			let resultIndex = 0;
			// 这里定义一函数,等计数器 和传进来的数组一致就 resolve
			function realutData(status, data) {
				if (status == 'rejected') {
					arr.push({
						status,
						reason: data
					});
				} else {
					arr.push({
						status,
						value: data
					});
				}
				if (++resultIndex === values.length) {
					resolve(arr);
				}
			}
			for (let i = 0; i < values.length; i++) {
				// values[i] 有可能是普通值 所以要包装一下,返回 Promise
				let result = Promise.resolve(values[i]);
				result.then(
					(data) => {
						realutData('fulfilled', data);
					},
					(err) => {
						realutData('rejected', err);
					}
				);
			}
		});
	}
}