我们都知道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到底是如何实现的呢?我们先来简单分析一下
- promise 是一个构造函数,默认需要传入一个executor执行器
- executor 会立刻执行,并且传入 resolve 和 reject 两个参数
- promise 有三个状态 fulfilled 成功 reject 拒绝态 pending等待态 (默认是等待态)
- 每个promise都有一个then方法 , 可以访问到成功的值和失败的原因
- 可以通过resolve和reject来改变状态,同时调用对应的回调, 一个promise实例状态变化后,不能再重新的发生变化
- 或者当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
就像这个例子,失败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);
}
);
}
});
}
}