Promise

207 阅读4分钟

更多文章

前言

promise作为日常开发的神兵利器,不但使代码更简洁,也能实现一些有意思的功能

promise要点

  1. 三个状态:pending(进行中)、fulfilled(成功)和rejected(失败)
  2. 状态由pending->fulfilledpending->rejected,状态一旦改变无法再改变
  3. then返回新的promise,状态将由新的promise决定
  4. finally总是返回原来的结果
  5. promise本身是同步,thencatchfinally属于异步微任务
  6. all是所有任务状态变为fulfilled才会变成fulfilled,一个任务为rejected状态则变为rejected
  7. race一个任务状态改变整个任务状态就发生变化
  8. allSettled无论单个任务状态是什么,整个任务状态只有fulfilled,返回结果如:[ { status: 'fulfilled', value: 42 }, { status: 'rejected', reason: -1 } ]
  9. any只有所有任务状态变为rejected状态才会变为rejected,有一个为fulfilled则为fulfilled

手写promise

代码简单,有注释不讲解了

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

function resolvePromise(promise2, x, resolve, reject) {
    // x即为then中返回的数据,可能为各种类型,这里需要判断
    // 循环引用报错
    if (x === promise2) {
        // reject报错
        return reject(new TypeError('Chaining cycle detected for promise'));
    }
    // 防止多次调用
    let called;
    // x不是null 且x是对象或者函数
    if (x != null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            // A+规定,声明then = x的then方法
            let then = x.then;
            // 如果then是函数,就默认是promise了
            if (typeof then === 'function') {
                // 就让then执行 第一个参数是this   后面是成功的回调 和 失败的回调
                then.call(x, y => {
                    // 成功和失败只能调用一个
                    if (called) return;
                    called = true;
                    // resolve的结果依旧是promise 那就继续解析
                    resolvePromise(promise2, y, resolve, reject);
                }, err => {
                    // 成功和失败只能调用一个
                    if (called) return;
                    called = true;
                    reject(err); // 失败了就失败了
                })
            } else {
                resolve(x); // 直接成功即可
            }
        } catch (e) {
            // 也属于失败
            if (called) return;
            called = true;
            // 取then出错了那就不要在继续执行了
            reject(e);
        }
    } else {
        resolve(x);
    }
}

class Promise {
    constructor(executor) {
        this.state = PENDING
        this.value = ''
        this.reason = ''

        // 成功存放的数组
        this.onResolvedCallbacks = [];
        // 失败存放法数组
        this.onRejectedCallbacks = [];

        let resolve = value => {
            if (this.state === PENDING) {
                this.state = FULFILLED
                this.value = value
                // pending->fulfilled 按照成功清单执行
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        let reject = reason => {
            if (this.state === PENDING) {
                this.state = REJECTED
                this.reason = reason
                // pending->rejected 按照异常清单执行
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }
    }
    then(onFulfilled, onRejected) {
        // onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        // onRejected如果不是函数,就忽略onRejected,扔出错误
        onRejected = typeof onRejected === 'function' ? onRejected : err => {
            throw err
        };
        let promise2 = new Promise((resolve, reject) => {
            if (this.state === FULFILLED) {
                // 异步解决:
                // onRejected返回一个普通的值,失败时如果直接等于 value => value,
                // 则会跑到下一个then中的onFulfilled中,
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);
            };
            if (this.state === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);
            };
            if (this.state === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                });
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e);
                        }
                    }, 0)
                });
            };
        });
        return promise2;
    }
}

执行顺序

这里看一个比较综合的demo,基本看懂了也不用去刷面试题了

async function test1() {
    console.log('test1'); 
    setTimeout(()=>console.log('timer1')) 
    return '1';
}
async function test2() {
    console.log('test2'); 
    setTimeout(()=>console.log('timer2')) 
    return Promise.resolve('2');
}
const p = ()=>new Promise(resolve=>'any')
async function test() {
    console.log('3'); 
    const v1 = await test1();
    console.log('v1: ', v1); 
    setTimeout(()=>console.log('timer3')) 
    const v2 = await test2();
    console.log('v2: ', v2); 
    const v3 = await p();
    console.log('all', v1, v2, v3); 
}
test();
const promise1 = new Promise((resolve) => {
    console.log('4'); 
    resolve('5');
})
promise1.then((val) => {
    console.log(val); 
})
Promise.resolve('6')
    .finally(()=>{
        console.log('7') 
        return '8'
    })
    .then(res=>console.log(res)) 
console.log('end'); 


// 执行结果: (顺序:值)
// 1-3
// 2-test1
// 3-4
// 4-end
// 5-v1: 1
// 6-test2
// 7-5
// 8-7
// 9-v2: 2
// 10-6 (这里打印6对应promise要点5,finally总返回原值)
// 11-timer1
// 12-timer1_p
// 13-timer3
// 14-timer2
// console.log('all', v1, v2, v3); 被p阻断,不执行
// 解析:
// 看代码的执行顺序步骤基本不变:
// 1. 从上往下 
// 2. 同步->微任务->宏任务
// 同步一定是立即执行的
// 微任务则看进入执行栈的顺序,按顺序执行
// 宏任务必定是最后执行,但执行前也需要看看是否有新的微任务加进来
// 不做逐个分析,但求能理解

经面

题目:

/**
 * 题目:JS 实现异步调度器
 * 要求:
 *  JS 实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有 2 个
 *  完善下面代码中的 Scheduler 类,使程序能正确输出
 */
class Scheduler {
  add(promiseCreator) {
    // ...
  }
  // ...
}
const timeout = (time) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, time);
  });
};
const scheduler = new Scheduler();
const addTack = (time, order) => {
  return scheduler
    .add(() => timeout(time))
    .then(() => console.log(order));
};
addTack(1000, '1');
addTack(500, '2');
addTack(300, '3');
addTack(400, '4');

// 输出:2 3 1 4
// 一开始,1、2 两个任务进入队列
// 500ms 时,完成 2,输出 2,任务 3 进队
// 800ms 时,完成 3,输出 3,任务 4 进队
// 1000ms 时,完成 1,输出 1,没有下一个进队的
// 1200ms 时,完成 4,输出 4,没有下一个进队的
// 进队完成,输出 2 3 1 4

答案:

// Scheduler如下,其余不变
class Scheduler {
    constructor(maxNum) {
        this.taskList = [];
        this.count = 0;
        this.maxNum = maxNum; // 最大并发数
    }
    async add(promiseCreator) {
        // 如果当前并发超过最大并发,那就进入任务队列等待
        if (this.count >= this.maxNum) {
            await new Promise((resolve) => {
                this.taskList.push(resolve);
            })
        }
        // 次数 + 1(如果前面的没执行完,那就一直添加)
        this.count++;
        // 等待里面内容执行完毕
        const result = await promiseCreator();
        // 次数 - 1
        this.count--;
        // 将队首出队
        if (this.taskList.length) {
            this.taskList.shift()();
        }
        // 链式调用,将结果值返回出去
        return result;
    }
}

更多经面

结语

活到老,学到老!!