js 异步系列(3) -promise中
2019-08-18 本文已影响0人
Super曲江龙Kimi
上一节promise上已经根据promise/A+规范写出了基本的Promise类的基本框架。
那么在promise/A+规范中还有几个关于then中的规范没有实现。
- 如果返回一个普通值,会走下一个then的成功回调
- 如果返回一个promise,会将他执行完的状态给下一个then
- 如果抛出错误,会走下一个then的reject方法
- 返回一个新的promise。实现链式调用
1. 返回一个新的promise。实现链式调用
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err};
// 因为then中的回调函数是异步执行的。为了确保newPromise存在,需要setTimeout
let newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
if (this.status === FULFILLED) {
// 如果执行then中报错,需要直接reject
try {
// 执行下then中的方法的返回值当成下一个then的参数传递
let x = onFulfilled(this.value);
// 返回值有多种情况。普通值或者还是一个promise
resolvePromise(newPromise, x, resolve, reject);
} catch(err) {
reject(err)
}
}
if (this.status === REJECTED) {
// 如果失败的then中有报错或者还返回一个promise直接向后传递即可
let x = onRejected(this.reason);
reject(x);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(newPromise, x, resolve, reject);
} catch(err) {
reject(err);
}
})
this.onRejectedCallbacks.push(() => {
let x = onRejected(this.reason);
reject(x);
})
}
})
})
return newPromise;
}
2. 如果then中执行后返回的还是一个promise
定义resolvePromise 函数来分情况处理then中返回的结果
const resolvePromise = (promise2, x, resolve, reject) => {
// 处理x的类型来决定下次then的状态是resolve还是reject
// promise2 === x的情况就相当于
// new Promise(resolve => resolve(1)).then(res => a1);
if (promise2 === x) {
return reject(new TypeError(`Chaining cycle detected for promise #<Promise>`));
}
let called = false;
// 判断x是不是一个普通函数
if (typeof x === 'object' && x !== null || typeof x === 'function') {
// 判断是否有then方法来判断是不是promise
try {
let then = x.then;
if (typeof then === 'function') {
// 是promise情况 使用then.call来执行x.then方法是为了避免有的对象写的只能获取一次。
// x.then需要再获取一次 而then.call是上次的缓存
then.call(x, y => {
// 参数可能还是promise需要递归
// .then(res => new Promise(resolve => resolve(new Promise..)))
resolvePromise(promise2, y, resolve, reject);
}, r => {
if (called) return; // 防止多次调用
called = true;
reject(r)
})
} else { // [1,2,3] {a:1}
resolve(x);
}
} catch(err) {
if (called) return; // 防止多次调用
called = true;
reject(err);
}
} else {
// 不是对象或者函数 普通值
resolve(x);
}
}
返回值为promise
如果then中返回promise,也是需要等待这个新的promise执行完毕。直到返回一个resolve()为普通值的promise为止。
new Promise(resolve => resolve(1)).then(res => {
return new Promise(resolve => resolve(res))
}).then(res => {console.log(2)});
// 所以return 一个new promise,也是要先调用它的then方法返回一个新的promise
// 返回的新的promise继续then讲返回值向后抛
// 和resolve(Promise...) 一样 都是相当于加了2个then
new Promise(resolve => resolve(1))
.then(res => res)
.then(res => res).then(res => res) // 这两个就相当于新增的2个
.then(res => {
console.log(2)
});
如果promise当作resolve()的参数或者在then中直接返回。则相当于多加了2次then
Promise.resolve() 和 Promise.reject()
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'));
但是参数是分4种情况的:
(1)参数是一个 Promise 实例
如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。
// resolve()参数还是promise则相当于多加两个then
const p = new Promise(resovle => setTimeout(resovle));
new Promise(resolve => resolve(p)).then(() => {
console.log("3");
});
p.then(() => {
console.log("1");
}).then(() => {
console.log("2");
});
// 1 2 3
// 而如果Promise.resolve() 参数中的Promise则是直接替换
const p = new Promise(resovle => setTimeout(resovle));
Promise.resolve(p).then(() => {
console.log("3");
});
p.then(() => {
console.log("1");
}).then(() => {
console.log("2");
});
// 3 1 2
(2)参数是一个thenable对象
thenable对象指的是具有then方法的对象,比如下面这个对象。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
上面代码中,thenable对象的then方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then方法指定的回调函数,输出 42。
(3)参数不是具有then方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。
const p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
等价于new Promise(resolve => resolve('Hello'));
(4)不带有任何参数
Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用Promise.resolve()方法。
const p = Promise.resolve();
p.then(function (res) {
// res undefined
});
上面代码的变量p就是一个 Promise 对象。