彻底搞懂Promise

271 阅读8分钟

常见的任务处理代码

//任务成功后,执行处理1,失败则执行处理2
pro.then(处理1).catch(处理2)

//任务成功后,依次执行处理1,处理2
pro.then(处理1).then(处理2)

//任务成功后,依次执行处理1,处理2,若任务失败或前面的处理有错,执行处理3
pro.then(处理1).then(处理2).catch(处理3)

promise的链式调用

新任务的状态取决于后续处理

  • 若没有相关的后续处理,新任务的状态和前任务一样,数据为前任务的数据
  • 若有后续处理但是还没执行(之前任务有异步处理),新任务的状态为pending
  • 后续处理执行了,看处理函数执行的过程
    • 处理函数正常运行,新任务为fulfilled,数据为后续处理的返回值

    • 处理函数报错,新任务为rejected,数据为异常对象

    • 处理函数返回一个任务对象,新任务的状态和数据与该任务对象一致

题1

const pro1 = new Promise((resolve) => {
    setTimeout(() => {
        resolve(1);
    }, 1000);
})

const pro2 = pro1.then((data) => {
    console.log(data);
    return data + 1;
});

const pro3 = pro2.then((data) => {
    console.log(data);
});

console.log(pro1, pro2, pro3);

setTimeout(() => {
    console.log(pro1, pro2, pro3);
}, 2000);

输出结果

截屏2022-03-07 下午1.07.48.png

分析

pro1一开始的状态是pending,由于pro2和pro3依赖pro1,所以

  • pro1 pending
  • pro2 pending
  • pro3 pending 过了一秒钟之后,pro1通过resolve达到fulfilled,pro1后续处理函数正常运行,返回值为1+1,所以pro2的状态是fulfilled,数据为2,同时输出1,pro2后续处理函数正常运行,但是没有返回值,所以pro3的状态为fulfilled,数据为undefined,同时输出2。

题2

new Promise((resolve, reject) => {
    resolve(1);
}).then((data) => {
    console.log(data);
    return 2;
}).catch((err) => {
    return 3;
}).then((data) => {
    console.log(data);
})

分析:

image.png pro1一开始就resolevd了,所以状态为fulfilled,数据为1。

pro2是pro1的后续处理,then是针对成功进行处理,pro1成功了,所以pro2要看他的处理函数,处理函数输出pro1的数据,返回2,那么2就是pro2的数据,同时pro2的状态变为fulfilled。

pro3是pro2的后续处理,由于catch是针对错误的,但是pro2没有对错误处理,所以pro3就是pro2的状态和数据

pro4是pro3的后续处理,then是针对成功的,看pro3的状态为fulfilled为成功,且处理函数没有错误,那么pro4就为fulfilled,数据为undefined。

  • pro1 fulfilled 1
  • pro2 fulfilled 2
  • pro3 fulfilled 2
  • pro3 fulfilled undefined

结果: 输出:1 2

题3

new Promise((resolve, reject) => {
    resolve();
}).then((data) =>{
    console.log(data.toString());
    return 2;
}).catch((data)=>{
    return 3;
}).then((data) => {
    console.log(data);
})

/**
 * pro1 fulfilled undefined
 * pro2 rejected 报错
 * pro3 fulfilled 3
 * pro4 fulfilled undefined
 */
 // 输出3

题4

new Promise((resolve, reject) => {
    throw new Error(1);
}).then((data) => {
    console.log(data);
    return new Error('2');
}).catch(err => {
    throw err;
    return 3;
}).then((res) => {
    console.log(res);
})

/**
 * pro1 rejected error(1)
 * pro2 rejected error(1)
 * pro3 rejected error(1)
 * pro4 rejected undefined
 */
 
 // 无输出,会有报错

题5

const pro1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject();
    }, 1000);
})

const pro2 = pro1.catch((err) => {
    return 2;
})

console.log(pro1, pro2);

setTimeout(() =>{
    console.log(pro1, pro2);
}, 2000)

/**
 * pro1 pending
 * pro2 pending
 * 
 * 1s后
 * pro1 rejected undefined
 * pro2 fulfilled 2
 */

Promise API

方法含义
Promise.resolve()返回一个完成状态的任务
Promise.reject()返回一个拒绝状态的任务
Promise.all(任务数组)返回一个任务,任务数组全部成功则成功,任何一个失败就失败
Promise.any(任务数组)返回一个任务,任务数组任意一个成功就成功,任务全部失败就失败
Promise.race(任务数组)返回一个任务,任务数组任意一个已决则已决,状态和其一致
Promise.allSettled(任务数组)返回一个任务,任务数组全部已决则成功,改任务不会失败
Promise.resolve(1).then((res) => {
    console.log(res);
})
// 相当于下面的代码
new Promise((resolve, reject) => {
    resolve(1)
}).catch((data)=>{
    console.log(data);
})
Promise.reject(1).catch((res) => {
    console.log(res);
})
// 相当于下面的代码
new Promise((resolve, reject) => {
    reject(1)
}).catch((data)=>{
    console.log(data);
})
  • Promise.all(任务数组)
const tesProArr = new Array(10).fill(0).map((item, index) => {
    if (Math.random() > 0.99) {
        return Promise.reject(index);
    }
    return Promise.resolve(index);
})
console.log(tesProArr);
const pro = Promise.all(tesProArr);
setTimeout(() => {
    console.log(pro);
    /**
     * Promise { [
            0, 1, 2, 3, 4,
            5, 6, 7, 8, 9
        ] }
     */
}, 1000);

任务数组全部成功则成功,返回的是成功的数据,若有失败的,则返回第一个失败的数据。

async

async关键字用于修饰函数,被它修饰的函数,一定返回Promise


async function test(){
    return 1;
}

async function test2(){
    return Promise.resolve(2);
}
// 上面这段代码相当于没有async
function test2(){
    return Promise.resolve(2);
}


async function test3(){
    throw new Error(3);
}

await注意点

async function test(){
    const a = await 1; // 相当于await Promise.resolve(1);
}

间隔duration打印数字1

function delay(duration){
    return new Promise((resolve, reject)=>{
        setTimeout(() =>{
            resolve(1);
        },duration)
    })
}

delay(1000).then(res=>{
    console.log(res);
})

宏任务和微任务

宏任务:setTimeout、setInterval、事件处理、requestAnimationFrame的回调。

微任务:Promise的then(catch)回调、MutationObserver

为什么要有微任务

按照官方的设想,任务之间是不平等的,有些任务对用户体验影响大,就应该优先执行,而有些任务属于背景任务(比如定时器),晚点执行没有什么问题,所以设计了这种优先级队列的方式。

一波输出题

const pro = new Promise((resolve, reject)=>{
    console.log(1);
    resolve();
    console.log(2);
})

pro.then(()=>{
    console.log(3);
})

console.log(4);

// 1,2,4,3
  • 分析:

pro: 执行栈: 宏任务: 微任务:pro.then

const pro = new Promise((resolve, reject) => {
    console.log(1);
    setTimeout(() => {
        console.log(2);
        resolve();
        console.log(3);
    })
})

pro.then(() => {
    console.log(4);
})

console.log(5);

// 1 5 2 3 4
const pro = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve();
    }, 1000);
})
const pro2 = pro.catch(()=>{
    return 2;
})

console.log(pro); // promise {<pending>}
console.log(pro2); // promise {<pending>}

setTimeout(() => {
    console.log(pro); // promise{<fulfilled> undefined}
    console.log(pro2); // promise {<fulfilled> undefined}
}, 2000)
async function method() {
    console.log(1);
    // await代码在then里面
    const a = await 2;
    console.log(a);
}
// function method() {
//     return Promise.resolve(1).then((a) => {
//         console.log(a);
//     })
// }
method();
console.log(3)

// 1 3 2
  • promise完成后才会到微队列,await后面的代码是异步的

async function m() {
    console.log(0);
    const n = await 1;
    console.log(n);
}

(async () => {
    await m();
    console.log(2);
})();
console.log(3);

/**
 * 执行m(),输出0,之后输入3,然后微队列中加入输出1,由于await 1的promise是完成状态的,输出1,然后输出2
 * 0,3,1,2
 */
async function m1() {
    return 1;
}

// console.log(m1()); // promise { 1 };

async function m2() {
    const n = await m1();
    console.log(n);
    return 2;
}

async function m3(){
    const n = m2();
    console.log(n);
    return 3;
}

m3().then((n)=>{
    console.log(n);
})

m3();

console.log(4);

/**
 * m1 fulfilled 1
 * m2 pending
 * m3 fulfilled 3
 * 
 * 微队列:输出1 输出3 输出1
 */

/**
 * pending pending 4 1 3 1 
*/
var a;
var b = new Promise((resolve, reject) => {
    console.log('promise1');
    setTimeout(() => {
        resolve();
    }, 1000);
}).then(() => {
    console.log('promise2');
}).then(() => {
    console.log('promise3');
}).then(() => {
    console.log('promise4');
})

a = new Promise((async (resolve, reject) => {
    console.log(a);
    await b;
    console.log(a);
    console.log('after1');
    // 由于a的状态是pending,没有resolve,所以后面代码不执行
    await a;
    resolve(true);
    console.log('after2');
}))

console.log('end');

// 分析
/**
 *  a: undefined  
 *  b: undefined  
 *  p1: pending
 *  p2: pending
 *  p3: pending
 *  p4: pending
 *  */

/**
 *  a: undefined  
 *  b: p4 promise{pending} 
 *  p1: pending
 *  p2: pending
 *  p3: pending
 *  p4: pending
 *  */

/**
 *  a: undefined  
 *  b: p4 promise{fulfilled}
 *  p1: fulfilled 
 *  p2: fulfilled
 *  p3: fulfilled
 *  p4: fulfilled
 *  p5: pending
 *  */


/** 输出
    promise1
    undefined
    end
    promise2
    promise3
    promise4
    Promise { <pending> }
    after1
*/
async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(function () {
    console.log('setTimeout');
}, 0)
async1();
new Promise((resolve, reject) => {
    console.log('promise1');
    resolve();
}).then(function () {
    console.log('promise2');
})
console.log('script end');

/**
 * 宏任务: setTimeout
 * 微队列:‘async1 end‘ ’promise2‘
 * script start
 * async1 start
 * async2
 * promise1
 * script end
 * async1 end
 * promise2
 * setTimeout
 */

手写Promise A+规范

//promise的状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

/**
 * 把callback放到微队列中
 * @param {Function} callback 
 */
function runMicroTask(callback) {
    if (process && process.nextTick) { // 在node环境
        process.nextTick(callback);
    } else if (MutationObserver) { // 在浏览器环境
        const observer = new MutationObserver(callback); // 优化:单例
        const p = document.createElement('p');
        observer.observe(p, {
            childList: true, //观察p元素的内部变化
        })
        p.innerHTML = '1';
    } else {
        setTimeout(callback, 0);
    }
}
/**
 * 判断obj是不是Promise
 * @param {*} obj 
 * @return {Boolean} boolean
 */
function isPromise(obj) {
    return !!(obj && typeof obj === 'object' && typeof obj.then === 'function');
}
class MyPromise {
    /**
     * 创建一个Promise
     * @param {Function} executor 任务执行器
     */
    constructor(executor) {
        this._status = PENDING;
        this._value = undefined;
        this._handler = []; // 存放后续处理函数
        try {
            // bind绑定当前this,避免外面使用时this的指向出问题
            executor(this._resolve.bind(this), this._reject.bind(this));
        } catch (error) {
            this._reject(error);
        }
    }
    /**
     * 改变Promise的状态和数据
     * @param {String} status 
     * @param {*} value
     */
    _changeStatus(status, value) {
        // 状态一旦改变,就不可以逆转
        if (this._status !== PENDING) {
            return;
        }
        this._status = status;
        this._value = value;
        // 状态发生改变,执行任务队列的
        this._runHandler();
    }
    /**
     * 将Promise推向成功的函数
     * @param {*} data 数据
     */
    _resolve(data) {
        this._changeStatus(FULFILLED, data);
    }
    /**
     * 将Promise推向失败的函数
     * @param {*} reason 
     */
    _reject(reason) {
        this._changeStatus(REJECTED, reason);
    }
    /**
     * 向队列中加入后续处理函数
     * @param {Function} executor 后续处理函数
     * @param {String} status 状态
     * @param {Function} resolve then返回Promise的resolve函数
     * @param {Function} reject then返回Promise的reject函数
     */
    _pushHandler(executor, status, resolve, reject) {
        this._handler.push({
            executor,
            status,
            resolve,
            reject,
        });
    }
    /**
     * 根据实际情况执行队列
     * @returns 
     */
    _runHandler() {
        if (this._status === PENDING) {
            // pending状态啥都不做
            return;
        }
        // 将任务队列中拿出来执行
        while (this._handler[0]) {
            const handler = this._handler[0];
            this._runOneHandle(handler);
            this._handler.shift();
        }
    }

    /**
     * 处理一个handler
     * @param {*} handler 
     */
    _runOneHandle({
        executor,
        status,
        resolve,
        reject
    }) {
        // 放入微队列中
        runMicroTask(() => {
            if (this._status !== status) {
                // 状态不一致
                return;
            }
            // 传递的后续处理不是一个函数
            if (typeof executor !== 'function') {
                // 那么要看当前状态
                this._status === FULFILLED ? resolve(this._value) : reject(this._value);
                return;
            }
            try {
                // 通过参数解构 解决this指向问题
                const data = executor(this._value);
                if (isPromise(data)) {
                    data.then(resolve, reject);
                } else {
                    resolve(data);
                }
            } catch (error) {
                reject(error);
            }

        })
    }
    /**
     * promise A+规范的then
     * @param {Function} onFulfilled 
     * @param {Function} onRejected
     * @return {Promise} promise
     */
    then(onFulfilled, onRejected) {
        return new MyPromise((resolve, reject) => {
            this._pushHandler(onFulfilled, FULFILLED, resolve, reject);
            this._pushHandler(onRejected, REJECTED, resolve, reject);
            // 加入到任务队列也要看是否执行
            this._runHandler();
        });
    }
    /**
     * 仅处理失败的场景
     * @param {*} onRejected 
     */
    catch (onRejected) {
        return this.then(null, onRejected);
    }

    /**
     * 无论什么情况,都要运行, 并且状态和数据和之前的pro一样,但是报错就报错
     * @param {Function} onSettled
     */
    finally(onSettled) {
        return this.then((data) => {
            onSettled();
            return data;
        }, (reason) => {
            onSettled();
            throw reason;
        });
    }

    static resolve(data) {
        // 传递的本身是ES6的Promise
        if (data instanceof MyPromise) {
            return data;
        }
        return new MyPromise((resolve, reject) => {
            if (isPromise(data)) {
                data.then(resolve, reject);
            } else {
                resolve(data);
            }
        })
    }

    static reject(reason) {
        return new MyPromise((resolve, reject) => {
            reject(reason);
        })
    }
    
}

手写catch和finally

Promise.prototype.catch = function (onRejected) {
    return this.then(null, onRejected);
}

Promise.prototype.finally = function (onSettled) {
    return this.then((data) => {
        onSettled();
        return data;
    }, (reason) => {
        onSettled();
        throw reason;
    })
}

Promise.resolve()和Promise.reject()

Promise.resolve = function (data) {
    // data本身是一个ES6Promise
    if (data instanceof Promise) {
        return data;
    }
    return new Promise((resolve, reject) => {
        function isPromise(obj) {
            return !!(obj && typeof obj === 'object' && typeof obj.then === 'function');
        }
         // 传递的Promise A+ 
        if (isPromise(data)) {
            data.then(resolve, reject);
        } else {
            resolve(data);
        }
    })
}

Promise.reject = function (reason) {
    return new Promise((resolve, reject) => {
        reject(reason);
    })
}

Promise.all()

Promise.all = function(arr){
    return new Promise((resolve, reject)=>{
        try{
            let count = 0; // Promise的个数
            let fulfilledCount = 0; // 已完成状态的Promise
            const result = [];
            // arr是一个迭代器 所以用forof循环
            for(const p of arr){
                let i = count;
                count++;
                Promise.resolve(p).then((data)=>{
                    fulfilledCount++;
                    result[i] = data;
                    if(fulfilledCount === count){
                        resolve(result);
                    }
                },reject)
            }
            if(count === 0){
                reject([]);
            }
        }catch(error){
           reject(error);
           console.error(error);
        }
    })
}

Promise.allSettled()

Promise.allSettled = function (pros) {
    const ps = [];
    for (const p of pros) {
        // ps数组的每一项都不可能失败,因为是自己写的
        ps.push(Promise.resolve(p).then(data => ({
            status: 'fulfilled',
            data
        }), reason => ({
            status: 'rejected',
            reason
        })))
    }
    return Promise.all(ps);
}

const pro = Promise.allSettled([1, Promise.resolve(1212), Promise.reject(234)]);

pro.then(res => {
    console.log(res);
    /** [
            { status: 'fulfilled', data: 1 },
            { status: 'fulfilled', data: 1212 },
            { status: 'rejected', reason: 234 }
        ]
    */
})

Promise.race()

Promise.race = function(pros){
    return new Promise((resolve, reject)=>{
        for (const p of pros) {
            Promise.resolve(p).then(resolve, reject);
        }
    })
}