前端技术专家面试-Promise&Generator

89 阅读4分钟

核心内容:语言对于复杂流程控制的探索 ifelse、switch、while能解决所有问题,但是不一定是合理的解。

=========

1. Promise

  • Promise提供了一种新的处理异步操作的方式:异步操作内容、不同状态的处理函数、操作状态;等待异步操作处理完以后,根据不同的状态调用对应的处理函数。
  • Promise可以通过链式的方式处理一串异步操作,简化了回调地狱问题;同时Promise提供错误处理,异步操作组合。
  • Promise.then 注册的函数是放在微任务里面调用的。
  • Promise的问题:很长的链式调用书写和维护都很麻烦。javascript对于复杂异步流程的控制问题没有根本解决。

2. 生成器函数(Generator Function)

2.1 普通函数(Function)与生成器函数(Generator Function)的比较

2.1.1 普通函数

  1. 执行流程:
    • 当函数被调用时,立即开始执行。
    • 函数体内的代码从上到下顺序执行,直到遇到 return 语句或函数末尾。
    • 执行完毕后,立即返回到调用处,继续执行调用函数的后续代码。
  2. 内部实现:
    • 调用时,在调用栈(call stack)中创建新的栈帧(stack frame)。
    • 栈帧包含函数的局部变量、参数和返回地址等信息。
    • 函数执行完毕后,栈帧被销毁,控制权返回给调用者。
  3. 特征:
    • 一旦开始执行,会一直运行到结束,中途不会暂停或让出控制权。
    • 每次调用都是独立的,不保留上次执行的状态。

2.1.2 生成器函数

  1. 执行流程:
    • 调用生成器函数时,不会立即执行函数体,而是返回一个生成器对象。
    • 通过调用生成器对象的 next() 方法,函数开始执行或从上次暂停处继续执行。
    • 执行到 yield 语句时,函数暂停并返回 yield 后的值。
    • 控制权返回给调用者,调用者可以处理 yield 的值并决定何时恢复生成器的执行。
  2. 内部实现:
    • 每次调用 next() 时,生成器函数的执行上下文被推入调用栈。
    • 函数执行到 yield 语句后,保存当前的执行状态(包括局部变量和程序计数器)。
    • 执行上下文从调用栈中弹出,但状态信息被保留在堆内存中。
    • 下次调用 next() 时,恢复保存的状态并继续执行。
  3. 特征:
    • 可以在执行过程中多次暂停和恢复。
    • 保留函数的执行状态,实现延迟计算和惰性求值。

2.2 Generator原本的目的

Generator函数最初的主要目的是创建自定义的迭代器。

迭代器是一种可以遍历集合的对象,而 Generator函数提供了一种简单的方法来创建这些迭代器。

在Python等于语言中都有类似的实现。

  • 生成有限或无限序列
  • 实现复杂的迭代逻辑
  • 惰性计算(只在需要时才生成下一个值)
  • 与语言的迭代机制(如 for...of 循环)无缝集成

Generator的特性决定了Generator可以很好的控制异步流程

2.3 Generator在控制流程方面的特性(被意外放大的作用)

其实还是基于可以在执行过程中多次暂停和恢复这个特性,可以结合Promise将异步控制流程书写的像同步一样。这个是在实际应用中被广大程序员发现的

问题:需要一个额外的控制函数。

// fetchUserData的实现已经很想在用同步的方式去写异步代码,
function* fetchUserData() {
    try {
        const user = yield fetch('/api/user').then(r => r.json());
        const posts = yield fetch(`/api/posts?userId=${user.id}`).then(r => r.json());
        const comments = yield fetch(`/api/comments?userId=${user.id}`).then(r => r.json());
        return { user, posts, comments };
    } catch (error) {
        console.error('Error fetching user data:', error);
    }
}

// 这个函数用来控制执行
function runGenerator(generatorFunction) {
    const generator = generatorFunction();

    function handle(result) {
        if (result.done) return Promise.resolve(result.value);

        return Promise.resolve(result.value).then(
            res => handle(generator.next(res)),
            err => handle(generator.throw(err))
        );
    }

    return handle(generator.next());
}
runGenerator(fetchUserData).then(data => console.log(data));

3. async/await

  • Generator和Promise可以来简化异步流程的书写,但是需要一个额外的控制函数(runGenerator),所以ES6就搞了一个语法糖来简化。
  • async/await 它提供了一种更简洁、更直观的方式来编写异步代码,而无需显式使用 Generator 或编写执行器函数
    • async 相当于声明 生成函数
    • await 相当于yield
  • 隐式的执行器:自动处理Promise的解析;恢复Generator的恢复。

3.1 async/await与事件循环