【每日前端面经】2024-03-17

【每日前端面经】2024-03-17

本期重点 —— Promise

欢迎订阅我的前端面经专栏:每日前端面经

Tips:

每日面经更新从 2-22 到 3-15 已有 23 篇,最近愈发觉得内容相似度高,并且理解程度不深  
于是临时停更面经,并将这些面经中的重难点以项目实战的方式展现出来供读者参阅

本期项目地址:https://github.com/xxhls/02-write-promise

在线预览:02-write-promise

产生背景

在 Promise 出现前,对于多个异步请求,往往会产生回调地狱

const fs = require('fs');

fs.readFile('./name.txt', 'utf-8', (err, data) => {
    fs.readFile(data, 'utf-8', (err, data) => {
        fs.readFile(data, 'utf-8', (err, data) => {
            console.log(data);
        });
    });
});

通过 Promise 可以有效解决以上问题

const fs = require('fs');

new Promise((resolve, reject) => {
    fs.readFile('./name.txt', 'utf-8', (err, data) => {
        if (err) reject(err);
        resolve(data);
    }).then((data) => {
        return new Promise((resolve) => {
            fs.readFile(data, 'utf-8', (err, data) => {
                resolve(data);
            });
        });
    }).then((data) => {
        return new Promise((resolve) => {
            fs.readFile(data, 'utf-8', (err, data) => {
                resolve(data);
            });
        });
    });
})

相关 API

Promise.all()

接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现,并返回一个包含所有兑现值的数组。如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因

示例
const promise1 = Promise.resolve(3);
const promise2 = Promise.resolve(4);
const promise3 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'foo'));

Promise.all([promise1, promise2, promise3]).then((values) => console.log(values));

// [3, 4, 'foo']
const promise1 = Promise.resolve(3);
const promise2 = Promise.reject(4);
const promise3 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'foo'));

Promise.all([promise1, promise2, promise3])
    .then((values) => console.log(values))
    .cache((reason) => console.log(reason));

// 4
手写
function PromiseAll(promises: Array<Promise>) {
    // 总共 Promise 的个数
    const total = promises.length;
    // 完成的 Promise 的个数
    let i = 0;
    // 存储 Resolve 结果
    const result = [];

    return new Promise((resolve, reject) => {
        // 逐一添加处理函数
        promises.forEach((promise) => {
            promise
                .then((data) => {
                    // 完成的 Promise 的个数加一
                    i++;
                    // Resolve 结果推入 result 中
                    result.push(data);
                    // 如果全部 Promise 都完成,就 Resolve 结果数组
                    if (i === total) resolve(result);
                })
                .catch((reason) => {
                    // 有一个 Promise 被拒绝直接 Reject 该原因
                    reject(reason);
                });
        });
    });
}
Promise.allSettled()

将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise。当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时),返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组

示例
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) =>
  setTimeout(reject, 100, 'foo'),
);
const promises = [promise1, promise2];

Promise.allSettled(promises).then((results) =>
  results.forEach((result) => console.log(result.status)),
);

// "fulfilled"
// "rejected"
手写
function PromiseAllSettled(promises: Array<Promise>) {
    // 总共 Promise 的个数
    const total = promises.length;
    // 完成的 Promise 的个数
    let i = 0;
    // 存储结果
    const result = [];

    return new Promise((resolve, reject) => {
        // 逐一添加处理函数
        promises.forEach((promise) => {
            promise
                .then((data) => {
                    // 完成的 Promise 的个数加一
                    i++;
                    // Resolve 结果推入 result 中
                    result.push("fulfilled");
                    // 如果全部 Promise 都完成,就 Resolve 结果数组
                    if (i === total) resolve(result);
                })
                .catch((reason) => {
                    // 完成的 Promise 的个数加一
                    i++;
                    // Resolve 结果推入 result 中
                    result.push("rejected");
                    // 如果全部 Promise 都完成,就 Resolve 结果数组
                    if (i === total) resolve(result);
                });
        });
    });
}
Promise.any()

将一个 Promise 可迭代对象作为输入,并返回一个 Promise。当输入的任何一个 Promise 兑现时,这个返回的 Promise 将会兑现,并返回第一个兑现的值。当所有输入 Promise 都被拒绝(包括传递了空的可迭代对象)时,它会以一个包含拒绝原因数组的 AggregateError 拒绝

示例
const promise1 = Promise.reject(0);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'quick'));
const promise3 = new Promise((resolve) => setTimeout(resolve, 500, 'slow'));

const promises = [promise1, promise2, promise3];

Promise.any(promises).then((value) => console.log(value));

// "quick"
const promise1 = Promise.reject(0);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'quick'));
const promise3 = new Promise((resolve, reject) => setTimeout(reject, 500, 'slow'));

const promises = [promise1, promise2, promise3];

Promise.any(promises).catch((reasons) => console.log(reasons));

// [0, "quick", "slow"]
手写
function PromiseAny(promises: Array<Promise>) {
    // 总共 Promise 的个数
    const total = promises.length;
    // 完成的 Promise 的个数
    let i = 0;
    // 存储拒绝结果
    const reasons = [];

    return new Promise((resolve, reject) => {
        // 逐一添加处理函数
        promises.forEach((promise) => {
            promise
                .then((data) => {
                    // 如果一个 Promise 完成,就 Resolve 数据
                    resolve(data);
                })
                .catch((reason) => {
                    // 拒绝的 Promise 的个数加一
                    i++;
                    // Reject 结果推入 reasons 中
                    reasons.push(reason);
                    // 如果全部 Promise 都拒绝,就 Reject 结果数组
                    if (i === total) reject(reasons);
                });
        });
    });
}
Promise.prototype.catch()

用于注册一个在 promise 被拒绝时调用的函数。它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 的方法

const promise1 = new Promise((resolve, reject) => {
  throw new Error('Uh-oh!');
});

promise1.catch((error) => {
  console.error(error);
});
// Error: Uh-oh!
Promise.prototype.finally()

Promise 实例的 finally() 方法用于注册一个在 promise 敲定(兑现或拒绝)时调用的函数。它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 方法

function checkMail() {
  return new Promise((resolve, reject) => {
    if (Math.random() > 0.5) {
      resolve('Mail has arrived');
    } else {
      reject(new Error('Failed to arrive'));
    }
  });
}

checkMail()
  .then((mail) => {
    console.log(mail);
  })
  .catch((err) => {
    console.error(err);
  })
  .finally(() => {
    console.log('Experiment completed');
  });
Promise.race()

接受一个 promise 可迭代对象作为输入,并返回一个 Promise。这个返回的 promise 会随着第一个 promise 的敲定而敲定

示例
const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

Promise.race([promise1, promise2]).then((value) => {
  console.log(value);
  // Both resolve, but promise2 is faster
});
// Expected output: "two"
手写
function PromiseRace(promises: Array<Promise>) {

    return new Promise((resolve, reject) => {
        // 逐一添加处理函数
        promises.forEach((promise) => {
            promise
                .then((data) => {
                    // 如果一个 Promise 完成,就 Resolve 数据
                    resolve(data);
                })
                .catch((reason) => {
                    // 如果一个 Promise 完成,就 Reject 数据
                    reject(data);
                });
        });
    });
}
Promise.reject()

返回一个已拒绝(rejected)的 Promise 对象,拒绝原因为给定的参数

function resolved(result) {
  console.log('Resolved');
}

function rejected(result) {
  console.error(result);
}

Promise.reject(new Error('fail')).then(resolved, rejected);
// Expected output: Error: fail
Promise.resolve()

将给定的值转换为一个 Promise。如果该值本身就是一个 Promise,那么该 Promise 将被返回;如果该值是一个 thenable 对象,Promise.resolve() 将调用其 then() 方法及其两个回调函数;否则,返回的 Promise 将会以该值兑现

该函数将嵌套的类 Promise 对象(例如,一个将被兑现为另一个 Promise 对象的 Promise 对象)展平,转化为单个 Promise 对象,其兑现值为一个非 thenable 值

const promise1 = Promise.resolve(123);

promise1.then((value) => {
  console.log(value);
  // Expected output: 123
});
Promise.prototype.then()

最多接受两个参数:用于 Promise 兑现和拒绝情况的回调函数。它立即返回一个等效的 Promise 对象,允许你链接到其他 Promise 方法,从而实现链式调用

const promise1 = new Promise((resolve, reject) => {
  resolve('Success!');
});

promise1.then((value) => {
  console.log(value);
  // Expected output: "Success!"
});

手写 Promise

const PENDING = Symbol('PENDING');
const FULLFILLED = Symbol('FULLFILLED');
const REJECTED = Symbol('REJECTED');

enum Status {
    PENDING,
    FULLFILLED,
    REJECTED
}

type Executor = (resolve: (value: any) => void, reject: (reason: any) => void) => void;

class MyPromise {
    status: Status;
    data: any;
    reason: any;

    onResolvedCallbacks: Function[];
    onRejectedCallbacks: Function[];

    constructor(executor: Executor) {
        this.status = Status.PENDING;
        this.data = null;
        this.reason = null;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];

        const resolve = (data: any) => {
            console.log('resolve');
            if (this.status === Status.PENDING) {
                this.status = Status.FULLFILLED;
                this.data = data;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        };

        const reject = (reason: any) => {
            console.log('reject');
            if (this.status === Status.PENDING) {
                this.status = Status.REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        };

        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }

    then(onFullfilled: (value: any) => any, onRejected: (reason: any) => any) {
        if (this.status === Status.FULLFILLED) {
            onFullfilled(this.data);
        } else if (this.status === Status.REJECTED) {
            onRejected(this.reason);
        } else if (this.status === Status.PENDING) {
            this.onResolvedCallbacks.push(() => {
                onFullfilled(this.data);
            });
            this.onRejectedCallbacks.push(() => {
                onRejected(this.reason);
            });
        }
    }
}

console.log("测试 MyPromise");
new MyPromise((resolve, reject) => {
    setTimeout(() => {
        console.log('执行完成');
        resolve('success');
    }, 1000);
}).then((data) => {
    console.log('回调成功');
    console.log(data);
}, (reason) => {
    console.log(reason);
});

new MyPromise((resolve, reject) => {
    setTimeout(() => {
        console.log('执行完成');
        reject('failure');
    }, 1000);
}).then((data) => {
    console.log('回调成功');
    console.log(data);
}, (reason) => {
    console.log('回调成功');
    console.log(reason);
});

export default MyPromise;
  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
《java面经-百度准入职老哥整理.pdf》是一份关于百度准入职面试的Java面经整理。这份面经是由百度准入职的老哥整理而成,其中记录了一些面试时可能会遇到的问题以及解答方法。 这份面经对于准备参加百度准入职面试的人来说非常有价值。首先,它列出了一些常见的面试问题,涵盖了Java语言的各个方面,包括基础知识、数据结构与算法、设计模式、多线程、网络编程等等。通过仔细研究和复习这些问题的答案,可以帮助面试者全面了解Java语言的特性和应用。 其次,这份面经还提供了问题的解答思路和方法,帮助面试者理清思路,正确回答问题。这对于很多面试者来说特别有帮助,因为在面试时有时会遇到一些棘手的问题,有了这份面经的指导,面试者可以更好地掌握应对策略。 不过需要注意的是,面经作为一份参考资料,不能完全依赖于它来准备面试面试官可能会问一些不在面经中列出的问题,因此考生还是需要自己对Java语言有充分的了解,并能够熟练运用。同时,面试官还会关注考生的沟通能力、解决问题的能力以及对新技术的学习和掌握能力。 总体来说,《java面经-百度准入职老哥整理.pdf》是一份非常宝贵的资料,可以帮助面试者对Java面试中可能会遇到的问题有更深入的了解,提供了解答思路和方法。但记住,面试准备还需要多方面的知识积累和实践经验的积累,才能在面试中展现自己的优势。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

糠帅傅蓝烧牛肉面

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值