JavaScript 同步异步机制和 Promise 原理实现笔记

题目描述

如何实现一个函数对象满足给出的含异步操作且严格限制调用顺序的链式函数调用过程?

arrange('aaa').wait(5).do('commit').waitFirst(3).execute();
// wait 和 waitFirst 是异步操作
// 要求输出为:start -> output `wait 5s` -> output `commit` -> output 'wait 3s' -> output 'aaa'

思路

由于要严格控制函数调用顺序,比如不允许arrange('aaa').execute()这样调用,因此容易想到定义arrange函数返回一个对象只包含后续能调用的函数wait。对于异步操作可以用 Promise 实现。怎么理解wait(5).do('commit')函数执行过程?是wait(5)执行了要等待5s后才能调用它返回的对象包含的do方法?运行过程当然是这样的!但要分清编写程序的时候是定义执行链的流程而不是运行时机,调用了wait(5)后可以立即调用do方法,表示定义了wait->do的流程,相当于给wait返回的对象注册了一个回调函数,让它在合适的时机执行,最终最后一个函数execute执行才真正把整个调用链上注册的函数都执行一遍,这也可以解释最后一个函数为啥命名为execute。可以通过维护一个执行队列来保存注册的回调函数,在execute调用时遍历执行队列,依次执行所有同步异步函数。看到execute函数执行输出了arrange('enzo')传入的参数,整个链式调用需要共享参数,这个可以通过共享一个上下文来简化参数传递,这个上下文可以设为arrange函数本身,通过JS闭包来捕获参数。arrange返回一个对象嵌套定义调用链中的每个函数,这样就控制了执行顺序。

代码v1

function arrange(value) {
   
   
  const queue = [];
  return {
   
   
    wait(delay) {
   
   
      queue.push(() => {
   
   
        console.log(`wait ${
     
     delay} s`);
      }); // 注册同步函数
      queue.push(async () => {
   
   
        // 注册异步函数
        await new Promise((resolve) => {
   
   
          setTimeout(() => {
   
   
            resolve(true);
          }, delay * 1000);
        });
      });

      return {
   
   
        // 返回对象控制函数执行顺序
        do(name) {
   
   
          queue.push(() => {
   
   
            console.log(name);
          }); // 注册同步函数
          return {
   
   
            waitFirst(delay2) {
   
   
              queue.push(() => {
   
   
                console.log(`wait ${
     
     delay2} s`);
              }); // 注册同步函数
              queue.push(async () => {
   
   
                // 注册异步函数
                await new Promise((resolve) => {
   
   
                  setTimeout(() => {
   
   
                    resolve(true);
                  }, delay2 * 1000);
                });
              });

              return  {
   
   
                async execute() {
   
   
                    for (const fn of queue) {
   
   
                        await fn();
                    }
                    console.log(value);
                }
              }
            },
          };
        },
      };
    },
  };
}


// test
arrange('aaa').wait(5).do('commit').waitFirst(3).execute();


思考

代码V1版本通过嵌套返回对象控制链式调用顺序,存在层级冗余、扩展不便的问题;可先基于状态模式将嵌套的方法定义扁平化,通过状态机显式管控调用顺序(替代隐式的嵌套约束),再借助迭代器模式封装任务队列的执行逻辑,解耦“链式规则控制”与“任务执行”,实现代码简化与扩展能力提升。

代码v2

class TaskIterator {
   
   
  constructor() {
   
   
    this.tasks = []; // 任务队列
  }

  addTask(task) {
   
   
    this.tasks.push(task);
  }

  async iterate() {
   
   
    for (const task of this.tasks) {
   
   
      await task();
    }
  }
}

function arrange(value) {
   
   
  // 状态机:控制调用顺序(初始→wait→do→waitFirst)
  const STATUS = {
   
   
    START: 0,
    WAIT_CALLED: 1,
    DO_CALLED: 2,
    WAIT_FIRST_CALLED: 3,
  };

  let curStatus = STATUS.START;
  const iterator = new TaskIterator();

  // 扁平化定义,无嵌套,方便扩展
  const api = {
   
   
    wait(delay) {
   
   
      if (curStatus !== STATUS.START) {
   
   
        throw new Error("Invalid order: wait must be called first!");
      }
      // 注册 wait 任务
      iterator.addTask(() => console.log(`wait ${
     
     delay} s`));
      iterator.addTask(async () => {
   
   
        await new Promise((resolve) => setTimeout(resolve, delay * 1000)<
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值