Promise作为异步编程方案callback的进阶版,解决了callback回调地域问题的同时,还增加了一些特性:比如all,race方法是的promise在处理异步编程时更加的强大和灵活。
Promise特性
- 构造器需要一个参数executor(执行器)函数,executor同时需要resolve和reject两个函数作为参数,resolve是将promise转换为fulfilled(成功态)时调用的函数,reject是将promise转换为rejected(失败态)时调用的函数。
- Promise具有三种状态:pending(默认态),fulfilled,rejected,只能由pending--->fulfilled或者pending--->rejected。fulfilled和rejected之间不能转换
- promise的then函数具有两个参数onFullfilled和onRejected两个回调函数,这两个回调函数会在promise确定了状态之后进行对应的调用
- onFullfilled和onRejected两个回调函数会被添加进微任务队列(延时调用,不过优先级高于setTimeout宏任务)
- executor里面如果有setTimeout延时调用,则promise会暂时处于pending状态,此时执行then函数时需要onFullfilledCallbacks和onRejectedCallbacks两个回调队列用于分别缓存对应的回调函数
- then函数会返回一个新的promise作为下一步的链式调用
根据以上特性初步实现一个最初的promise,代码如下:
class Promise {
constructor(executor) {
this.status = 'pending' //默认状态
this.value = undefined; //成功时保存resolve传过来的参数
this.reason = undefined; //失败时保存reject传过来的原因
this.onFullfilledCallbacks = [];//成功时回调队列
this.onRejectedCallbacks = [];//失败时回调队列
let resolve = (data) => {
this.status = 'fullfilled';
this.value = data;
this.onFullfilledCallbacks.forEach(fn => fn());
}
let reject = (err) => {
this.status = 'rejected';
this.reason = err;
this.onRejectedCallbacks.forEach(fn => fn());
}
try {
executor(resolve, reject);
} catch (err) {//执行器如果报错直接转为失败态,并且将失败原因传出
reject(err);
}
}
then(onFullfilled, onRejected) {
let promise2;//返回新的promise
if(this.status === 'fullfilled') {//如果成功了
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFullfilled(this.value);
resolve(x);
} catch (err) {//失败将错误传出
reject(err);
}
}, 10);
});
}
if(this.status === 'rejected') {//如果成功了
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolve(x);//即使失败了下一次then依然会走进成功的回调函数
} catch (err) {//失败将错误传出
reject(err);
}
}, 10);
});
}
if(this.status === 'pending') {//pending状态需要将对应的回调缓存起来
promise2 = new Promise((resolve, reject) => {
this.onFullfilledCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFullfilled(this.value);
resolve(x);
} catch (err) {//失败将错误传出
reject(err);
}
}, 10);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolve(x);//即使失败了下一次then依然会走进成功的回调函数
} catch (err) {//失败将错误传出
reject(err);
}
}, 10);
});
});
}
return promise2;
}
}
复制代码
then链式调用实现
按照promise A+ 规范,返回值x需要进行以下解析处理:
1. x 与 promise 相等
如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
2. x 为 Promise
如果 x 为 Promise ,则使 promise 接受 x 的状态:
- 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
- 如果 x 处于执行态,用相同的值执行 promise
- 如果 x 处于拒绝态,用相同的据因拒绝 promise
3. x 为对象或函数
- 把 x.then 赋值给 then
- 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
- 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
- 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
- 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
- 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
- 如果调用 then 方法抛出了异常 e:
- 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
- 否则以 e 为据因拒绝 promise
- 如果 then 不是函数,以 x 为参数执行 promise
4. 如果 x 不为对象或者函数
以 x 为参数执行 promise
resolvePromise代码实现
function resolvePromise(promise2, x, resolve, reject) {
let called = false;
if(promise2 === x) {// x 与 promise 相等
return reject(new TypeError('循环引用了')); //抛出错误
}
if(x instanceof Promise) {
if(x.status === 'pending') {//如果 x 处于等待态
x.then(function(value) {
resolvePromise(promise2, value, resolve, reject);// promise 需保持为等待态直至 x 被执行或拒绝
}, reject);
} else {//如果 x 处于执行态或者拒绝态
x.then(resolve, reject);
}
return;
}
if(x !== null && (typeof x === 'object' || typeof x === 'function')) {//x 为对象或函数
try {
let then = x.then;
if(typeof then === 'function') {//如果 then 是函数
then.call(x, function(y) {//resolvePromise以值 y 为参数被调用
if(called) {//如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次
return; //则优先采用首次调用并忽略剩下的调用
}
called = true;
resolvePromise(promise2, y, resolve, reject);//运行 [[Resolve]](promise, y)
}, function(r) {//如果 rejectPromise
if(called) {//如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次
return; //则优先采用首次调用并忽略剩下的调用
}
called = true;
reject(r);//以 r 为原因拒绝 promise
});
} else {//如果 then 不是函数
resolve(x);//以 x 为参数执行 promise
}
} catch (err) {//如果取 x.then 的值时抛出错误
if(called) {//如果 resolvePromise 或 rejectPromise 已经被调用
return; //忽略之
}
called = true;
reject(err);//以 e 为原因拒绝 promise
}
} else {//如果 x 不为对象或者函数
resolve(x);//以 x 为参数执行 promise
}
}
复制代码
其他补充
1. Promise的then链式调用还有个特点,就是在中间then没有传入参数时可以依次传递数据,例如:
Promise.resolve('aaaa').then().then().then(function(data) {console.log(data)});
这个特性是因为onFullfilled和onRejected在没有被传入时会被赋予默认值,如下:
if(typeof onFullfilled !== 'function') {
onFullfilled = value => value;
}
if(typeof onRejected !== 'function') {
onRejected = err => {
throw err;
}
}
复制代码
2. Promise实例的catch方法类似不传onFullfilled参数的then方法,实现如下:
catch(callback) {
return this.then(null, callback);
}
复制代码
3. Promise的静态all方法能够在传入的所以promise成功后返或数据,其中有一个失败则全部失败。
static all(promises) {
let dataArr = [];
let len = promises.length;
let count = 0;//统计成功次数
return new Promise(function(resolve, reject) {
for (let i = 0;i < len;i++) {
p.then(function(data) {
dataArr[i] = data;
if(++count === len) {//所有promise成功
resolve(dataArr);
}
}, reject);
}
});
}
复制代码
4. Promise的静态race方法使得传入的所以promise中最先成功的那个结果返回,有一个成功就成功
static race(promises) {
let len = promises.length;
return new Promise(function(resolve, reject) {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject);
}
});
}
复制代码
完整的Promise实现
class Promise {
constructor(executor) {
this.status = 'pending' //默认状态
this.value = undefined; //成功时保存resolve传过来的参数
this.reason = undefined; //失败时保存reject传过来的原因
this.onFullfilledCallbacks = [];//成功时回调队列
this.onRejectedCallbacks = [];//失败时回调队列
let resolve = (data) => {
this.status = 'fullfilled';
this.value = data;
this.onFullfilledCallbacks.forEach(fn => fn());
}
let reject = (err) => {
this.status = 'rejected';
this.reason = err;
this.onRejectedCallbacks.forEach(fn => fn());
}
try {
executor(resolve, reject);
} catch (err) {//执行器如果报错直接转为失败态,并且将失败原因传出
reject(err);
}
}
then(onFullfilled, onRejected) {
let promise2;//返回新的promise
if(typeof onFullfilled !== 'function') {
onFullfilled = value => value;
}
if(typeof onRejected !== 'function') {
onRejected = err => {
throw err;
}
}
if(this.status === 'fullfilled') {//如果成功了
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFullfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (err) {//失败将错误传出
reject(err);
}
}, 10);
});
}
if(this.status === 'rejected') {//如果成功了
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);//即使失败了下一次then依然会走进成功的回调函数
} catch (err) {//失败将错误传出
reject(err);
}
}, 10);
});
}
if(this.status === 'pending') {//pending状态需要将对应的回调缓存起来
promise2 = new Promise((resolve, reject) => {
this.onFullfilledCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFullfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (err) {//失败将错误传出
reject(err);
}
}, 10);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);//即使失败了下一次then依然会走进成功的回调函数
} catch (err) {//失败将错误传出
reject(err);
}
}, 10);
});
});
}
return promise2;
}
catch(callback) {
return this.then(null, callback);
}
static all(promises) {
let dataArr = [];
let len = promises.length;
let count = 0;//统计成功次数
return new Promise(function(resolve, reject) {
for (let i = 0;i < len;i++) {
p.then(function(data) {
dataArr[i] = data;
if(++count === len) {//所有promise成功
resolve(dataArr);
}
}, reject);
}
});
}
static race(promises) {
let len = promises.length;
return new Promise(function(resolve, reject) {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject);
}
});
}
static deferred() {
let dfd = {};
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
}
function resolvePromise(promise2, x, resolve, reject) {
let called = false;
if(promise2 === x) {// x 与 promise 相等
return reject(new TypeError('循环引用了')); //抛出错误
}
if(x instanceof Promise) {
if(x.status === 'pending') {//如果 x 处于等待态
x.then(function(value) {
resolvePromise(promise2, value, resolve, reject);// promise 需保持为等待态直至 x 被执行或拒绝
}, reject);
} else {//如果 x 处于执行态或者拒绝态
x.then(resolve, reject);
}
return;
}
if(x !== null && (typeof x === 'object' || typeof x === 'function')) {//x 为对象或函数
try {
let then = x.then;
if(typeof then === 'function') {//如果 then 是函数
then.call(x, function(y) {//resolvePromise以值 y 为参数被调用
if(called) {//如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次
return; //则优先采用首次调用并忽略剩下的调用
}
called = true;
resolvePromise(promise2, y, resolve, reject);//运行 [[Resolve]](promise, y)
}, function(r) {//如果 rejectPromise
if(called) {//如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次
return; //则优先采用首次调用并忽略剩下的调用
}
called = true;
reject(r);//以 r 为原因拒绝 promise
});
} else {//如果 then 不是函数
resolve(x);//以 x 为参数执行 promise
}
} catch (err) {//如果取 x.then 的值时抛出错误
if(called) {//如果 resolvePromise 或 rejectPromise 已经被调用
return; //忽略之
}
called = true;
reject(err);//以 e 为原因拒绝 promise
}
} else {//如果 x 不为对象或者函数
resolve(x);//以 x 为参数执行 promise
}
}
复制代码