1、Promise&&then
我们知道,promise是为了解决异步回调地狱而出现的新的解决方案,它是主要特点是可以通过then拿到异步的结果,then又能返回新的异步结果,形成链式调用
javascript
new Promise((resolve,reject)=>{
resolve('结果')
}).then(res=>{
return res
}).then(...)
我们也知道,在promise中有三个状态,pending进行中,fulfilled已完成,rejected已失败,且一旦pending改变,不会再被更改。
所以先根据以上信息,实现:可以通过then链式调用
ini
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class myPromise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
// this.onFulfilledCbs = [];
// this.onRejectedCbs = [];
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value
// this.onFulfilledCbs.forEach(cb => cb(value))
}
}
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason
// this.onRejectedCbs.forEach(cb => cb(reason))
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
return new myPromise((resolve, reject) => {
let result = undefined;
if (this.status === FULFILLED) {
result = onFulfilled(this.value)
resolve(result)
}
if (this.status === REJECTED) {
result = onRejected(this.reason)
reject(result)
}
// if (this.status === PENDING) {
// this.onFulfilledCbs.push(onFulfilled)
// this.onRejectedCbs.push(onRejected)
// }
})
}
}
new myPromise((resolve, reject) => {
// setTimeout(() => {
resolve(111)
// }, 1000)
}).then(res => {
console.log('res', res)
})
根据以上代码,在then中再return promise,实现了简单的链式调用,且需要setTimeout模拟异步,在then中拿到异步操作返回的结果,因为是异步的,当执行then时status还是pending,需要把成功和失败回调存储,等到resolve或reject时拿出来执行。(这部分代码实现把以上注释部分取消即可。)
根据promise的约定,resolve接收的参数可以有4种,所以需要兼容一下,目前只有一种就是基础值,还有promise、thenable对象、空,且resolve出的值不能是promise自身,否则会造成无限循环引用。
同时用setTimeout将异步模拟一下,有如以下代码:
ini
// promise有三种状态,分别是pending.FULFILLED ,rejected,pending是初始状态,FULFILLED 是成功状态,rejected是失败状态
// 状态一旦改变,不会再变
const PENDING = "pending";
const FULFILLED = "FULFILLED ";
const REJECTED = "rejected";
class Promise1 {
constructor(excutor) {
// 其实resolve,reject这两个函数不是他们传来的,只是他们调用的内部的方法,它们传来的只是值
this.status = PENDING;
this.value = undefined;
this.error = undefined;
this.onFulfilledCallbacks = []; // 存储then方法中onFULFILLED 的回调函数
this.onRejectedCallbacks = []; // 存储then方法中onRejected的回调函数
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach((callback) => callback());
}
};
const reject = (error) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.error = error;
this.onRejectedCallbacks.forEach((callback) => callback());
}
};
try {
excutor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
// 处理onFULFILLED 不是函数的情况
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
// 处理onRejected不是函数的情况
onRejected =
typeof onRejected === "function"
? onRejected
: (error) => {
throw error;
};
// then最终还是会return一个promise
const promise = new Promise((resolve, reject) => {
const handleFulfilled = () => {
try {
const result = onFulfilled(this.value);
resolvePromise(promise, result, resolve, reject);
} catch (error) {
reject(error);
}
};
const handleRejected = () => {
try {
const result = onRejected(this.error);
resolvePromise(promise, result, resolve, reject);
} catch (error) {
reject(error);
}
};
if (this.status === FULFILLED) {
// 为什么这里需要setTimeout,因为要模拟promise是异步的
setTimeout(handleFulfilled, 0);
} else if (this.status === REJECTED) {
setTimeout(handleRejected, 0);
} else {
this.onFulfilledCallbacks.push(() => setTimeout(handleFulfilled, 0));
this.onRejectedCallbacks.push(() => setTimeout(handleRejected, 0));
}
});
return promise;
}
}
function resolvePromise(promise, result, resolve, reject) {
// 如果promise和result是同一个对象,则抛出TypeError,
if (promise === result) {
return reject(new TypeError("Chaining cycle detected for promise"));
}
// 如果result是thenable对象
if (result && (typeof result === "object" || typeof result === "function")) {
try {
const then = result.then;
if (typeof then === "function") {
// then就是接收两个参数:resolve或reject,然后出结果的函数,直接执行它,但注意this指向,并传参
then.call(
this,
(value) => {
resolvePromise(promise, value, resolve, reject);
},
(reason) => reject(reason)
);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
} else {
// 否则直接调用resolve
resolve(result);
}
}
2、catch
then后面除了可以跟着then,也可以通过catch方法捕获错误,它等同于then中的第二个参数,所以通过调用then,将catch中的回调函数传给then第二个参数即可。
kotlin
// 声明在Promise类中
catch(cb){
return this.then(_,cb)
}
3、finally
Promise的实例方法还有一个叫finally,是不管成功还是失败都会执行的,还是用then方法返回结果,不管成功还是失败结果都resolve返回一个promise,从而catch后面也可继续链式调用。then接收两个参数,成功后的回调,这个回调的参数是成功返回的值,所以finally若想
javascript
// 声明在Promise类中
finally(cb){
return this.then(
value=>Promise1.resolve(cb()).then(()=>value)
reason=>Promise1.resolve(cb()).then(()=>reason)
)}
4、resolve
在Promise类上,有resolve,reject,race,all,allSettled等类方法,接下来一一演示,Promise.resolve(1)就是等同于new Resolve(()=>{resolve(1)}),既然如此直接返回后者,再加个判断,如果不是promise才需要包装,是promise直接返回
scss
static resolve(value){
if(value instanceof Promise1){
return value;
}
return new Promise1(()=>resolve(value))
}
5、reject
reject同理可得,Promise.reject('err')就是等同于new Promise1(()=>{reject('err')})
javascript
static reject(reason){
return new Promise1((_,reject)=>reject(reason))
}
6、race
race、all、allSettled都是为了解决多个promise同时执行的,race是当其中一个先执行完毕,多个promise实例的执行结果就是这个最先完成的状态。 既然是多个,那参数就是数组,我们只需去遍历,最先执行resolve的,也就是最先改变状态的,后续即使继续遍历,也不再改变状态。
怎么知道它何时resolve呢,因为我们知道then中的回调是在成功时调用的,所以通过Promise.resolve包装,在then中去resolve实例
javascript
static race(arr){
return new Promise1((resolve,reject)=>{
arr.forEach(pro=>{
Promise1.resolve(pro).then((value)=>{
resolve(pro)
},reason=>{
reject(reason)
})
})
})
}
7、all
all是多个promise实例,只要有一个失败,结果就是失败,全部成功才算成功。所以要计算成功的次数,数组长度等于成功的个数后,再resolve结果数组。
ini
static all(arr) {
return new Promise1((resolve, reject) => {
let res = [];
let completeCount = 0;
if (arr.length === 0) {
resolve(res);
return;
}
arr.forEach((pro) => {
Promise1.resolve(pro).then(
(value) => {
res[completeCount] = value;
completeCount++;
if (completeCount === arr.length) {
resolve(res);
}
},
(err) => {
reject(err);
}
);
});
});
}
}
8、allSettled
allSettled是不管成功还是失败,都会返回结果的。注意不管当前是成功还是失败,当执行到最后一个时,都是resolve出数组结果,且结果数组对象是由status和结果值组成的,如果成功status='fulfilled',失败status='rejected'
ini
static allSettled(promises) {
return new CustomPromise((resolve, reject) => {
const results = [];
let completedCount = 0;
if (promises.length === 0) {
resolve(results);
return;
}
promises.forEach((promise, index) => {
CustomPromise.resolve(promise).then(
(value) => {
results[index] = {
status: "fulfilled",
value,
};
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
},
(reason) => {
results[index] = {
status: "rejected",
reason,
};
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
}
);
});
});
}
根据以上,留一个作业,any方法,只要有一个成功,包装实例返回结果fulfilled,或者全部都失败,结果才会是rejected