Promise详解(包含手写promise)

1、回调地狱的烦恼

谈到回调地狱,举个生活中的例子

有一天,小明去自助烤肉吃饭,由于自助餐不允许浪费,所有小明每次都把拿到的东西吃完

然后看有没有吃饱,如果没有吃饱,就继续拿餐,吃饱了就停止

上述场景,使用程序实现如下

typescript 复制代码
const sendMessage = (food: string, onFulfilled: () => void, onRejected: () => void) => {
    console.log(`烤肉店,去拿${food}...`);
    console.log(`干饭中...`);
    setTimeout(() => {
      //模拟是否吃饱
      if (Math.random() <= 0.1) {
        console.log(`吃了${food}`);
        onFulfilled();
      } else {
        console.log(`吃了${food}`);
        onRejected();
      }
    }, 1000);
  };
  sendMessage(
    "五花肉",
    () => {
      console.log("吃饱了...");
    },
    () => {
      console.log("没吃饱..");
      sendMessage(
        "牛肉",
        () => {
          console.log("吃饱了...");
        },
        () => {
          console.log("没吃饱..");
          sendMessage(
            "鸡翅",
            () => {
              console.log("吃饱了...");
            },
            () => {
              console.log("没吃饱...");
              console.log("没食物了...");
            }
          );
        }
      );
    }
  );

上述代码非常恶心,每一次'没吃饱'都需要在onRejected中作回调处理,如果一直没吃饱,就会一直拿东西,一直在onRejected中作回调处理,代码完全没法读了。

小结

回调地狱就是在回调函数里面再次嵌套回调函数

要解决这样的回调地狱问题,就需要promise出马

2、Promise规范

Promise是一套专门处理异步场景的规范,它能有效的避免回调地狱的产生,使异步代码更加清晰、简洁、统一

这套规范最早诞生于前端社区,规范名称为 Promise A+

该规范出现后立即得到了很多开发者的响应

Promise A+ 规定:

  • 所有的异步场景,都可以看作一个异步任务,每个异步任务,在js中应该表象为一个对象,该对象之为promise对象,也叫任务对象
classDiagram class Promise对象1{ 网络通信请求 } class Promise对象2{ 网络通信请求 } class Promise对象{ ..... }
  • 每个任务对象,都应该有两个阶段,三个状态
classDiagram class 未决阶段unsettled{ pending 挂起状态 } class 已决阶段settled{ fulfilled 完成状态 rejected 失败状态 } 未决阶段unsettled --|> 已决阶段settled

它们之间有以下逻辑

任务总是从未决阶段变到已决阶段,无法逆行

任务总是从挂起状态变到完成或者失败状态,无法逆行

状态不能倒流,任务一旦完成或者失败,状态就固定下来,永远无法改变

  • 挂起 -> 完成,称为 resolve; 挂起 -> 失败 称为reject。任务完成时,可能有一个相关数据;任务失败时,可能有一个失败原因
classDiagram class 未决阶段unsettled{ pending 挂起状态 } class 已决阶段settled{ fulfilled 完成状态 rejected 失败状态 } 未决阶段unsettled --|> 已决阶段settled:resolved(data)改变完成状态 未决阶段unsettled --|> 已决阶段settled:reject(reason)改变失败状态
  • 可以针对任务进行后续处理,针对完成状态的后续处理称为onFulfilled,针对失败的后续处理为onRejected
classDiagram class 未决阶段unsettled{ pending 挂起状态 } class 已决阶段settled{ fulfilled 完成状态 rejected 失败状态 } class 后续任务完成后处理data { onFulfilled() onRejected() } 未决阶段unsettled --|> 已决阶段settled:resolved(data)改变完成状态 未决阶段unsettled --|> 已决阶段settled:reject(reason)改变失败状态 已决阶段settled --|> 后续任务完成后处理data:onFulfilled 已决阶段settled --|> 后续任务完成后处理data:rejected

以上就是整个 Promise A+规范的模型

3、Promise API

ES6提供了一套API,实现了Promise A+ 规范

基本使用如下

javascript 复制代码
  const promise = new Promise((resolve, reject) => {
    //任务具体执行流程,该函数会立即执行
    //调用resolve(data),可将任务变为fulfilled状态,data为需要传递的相关数据
    //调用reject(reason),可将任务变为rejected状态,reason为需要传递的原因
  });

  promise.then(
    (data) => {
      // onFulfilled 函数,当任务完成后自动运行该函数,data为任务完成时的相关数据
    },
    (reason) => {
        // onRejected 函数 ,当任务失败后,会自动运行该函数,reason为任务失败的相关原因
    }
  );

通过Promise改造上面代码

typescript 复制代码
  const sendMessage = (food: string) => {
    return new Promise((resolve, reject) => {
      console.log(`烤肉店,去拿${food}...`);
      console.log(`干饭中...`);
      setTimeout(() => {
        //模拟是否吃饱
        if (Math.random() <= 0.1) {
          resolve(`吃了${food},吃饱了`);
        } else {
          reject(`吃了${food},没吃饱`);
        }
      }, 1000);
    });
  };
  sendMessage("五花肉").then(
    (res) => {
      console.log(res);
    },
    (error) => {
      console.log(error);
    }
  );

上面代码还是很恶心,没有解决回掉地狱问题,不着急,接着往下学习

补充一个API catch

.catch(onRejected) = .then(null,onRejected)

4、链式调用

graph TD Promise1 -->|API| then1 onFulfilled1 -->|处理成功数据data|then1 onRejected1 -->|处理失败原因reason|then1 then1 -->|then方法调用返回一个新的Promise| Promise2 Promise2 -->|API| then2 onFulfilled2 -->|处理成功数据data|then2 onRejected2 -->|处理失败原因reason|then2 then2 -->|then方法调用返回一个新的Promise| Promise3
  • then方法必定会返回一个新的Promise,可理解为后续处理也是一个Promise任务

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

若没有相关后续处理,新任务状态和前任务一致,数据为前任务的数据

若有后续处理但还未执行,新任务挂起

若后续处理执行了,则根据后续处理的情况确定新任务的状态

复制代码
后续处理执行无错,新任务的状态为完成,数据为后续处理的返回值
后续处理执行有错,新任务的状态为失败,数据为异常对象
后续执行后返回的是一个任务对象,新任务的状态和数据对象与该任务一致

由于链式调用任务的存在,异步代码拥有了更强的表达力

arduino 复制代码
 //  常见任务处理代码
  
 /*
  *   任务成功,执行处理1,失败执行处理2
  */
  
 //   promise.then(处理1).catch(处理2);
 //   promise.then(处理1).catch(处理2);

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

有了链试调用,上述案例代码继续优化

javascript 复制代码
const sendMessage = (food: string) => {
   return new Promise((resolve, reject) => {
     console.log(`烤肉店,去拿${food}...`);
     console.log(`干饭中...`);
     setTimeout(() => {
       //模拟是否吃饱
       if (Math.random() <= 0.1) {
         resolve(`吃了${food},吃饱了`);
       } else {
         reject(`吃了${food},没吃饱`);
       }
     }, 1000);
   });
 };
 sendMessage("五花肉")
   .catch((error) => {
     console.log(error);
     return sendMessage("牛肉");
   })
   .catch((error) => {
     console.log("鸡翅");
     return sendMessage("鸡翅");
   })
   .then(
     (res) => {
       console.log(res);
     },
     (error) => {
       console.log(error, "没吃的了");
     }
   );

终于通过链式调用解决回掉地狱问题了。

5、Promise的静态方法

好了,小明吃饭的问题解决了,吃完饭回家后,又出现新的问题了

小明的媳妇给小明交代了两个任务

  • 洗衣服(交给洗衣机完成)

  • 拖地(交给扫地机器人完成)

小明需要在所有任务完成之后汇到,哪些做好了,哪些没有做好。

为了最大程度的节约时间,小明希望这些任务同时进行,最终一起汇报结果统一处理

每个任务可以看做是一个返回Promise的函数

javascript 复制代码
 const wash = () => {
   return new Promise((resolve, rejected) => {
     console.log("小明打开了洗衣机");
     setTimeout(() => {
       if (Math.random() < 0.5) {
         resolve("衣服洗好了");
       } else {
         rejected("忘记加水了,把衣服整烂了");
       }
     }, 1000);
   });
 };
 const sweep = () => {
   return new Promise((resolve, rejected) => {
     console.log("小明打开了扫地机器人");
     setTimeout(() => {
       if (Math.random() < 0.5) {
         resolve("地扫好了");
       } else {
         rejected("机器人坏了,没有扫地");
       }
     }, 2000);
   });
 };

串行执行,先洗衣服,衣服洗完之后在扫地

javascript 复制代码
const wash = () => {
   return new Promise((resolve, rejected) => {
     console.log("小明打开了洗衣机");
     setTimeout(() => {
       if (Math.random() < 0.5) {
         resolve("衣服洗好了");
       } else {
         rejected("忘记加水了,把衣服整烂了");
       }
     }, 1000);
   });
 };
 const sweep = () => {
   return new Promise((resolve, rejected) => {
     console.log("小明打开了扫地机器人");
     setTimeout(() => {
       if (Math.random() < 0.5) {
         resolve("地扫好了");
       } else {
         rejected("机器人坏了,没有扫地");
       }
     }, 2000);
   });
 };
 wash().then(() => {
   sweep();
 });

同时执行,洗衣服,扫地

javascript 复制代码
const wash = () => {
   return new Promise((resolve, rejected) => {
     console.log("小明打开了洗衣机");
     setTimeout(() => {
       if (Math.random() < 0.5) {
         resolve("衣服洗好了");
       } else {
         rejected("忘记加水了,把衣服整烂了");
       }
     }, 1000);
   });
 };
 const sweep = () => {
   return new Promise((resolve, rejected) => {
     console.log("小明打开了扫地机器人");
     setTimeout(() => {
       if (Math.random() < 0.5) {
         resolve("地扫好了");
       } else {
         rejected("机器人坏了,没有扫地");
       }
     }, 2000);
   });
 };
 wash();
 sweep();

并行执行有一个严重的问题,没法办汇总任务状态。

针对此问题,Promise提供了静态方法

Promise.resolve(data) 直接返回一个完成状态的任务

Promise.reject(reason) 直接返回一个拒绝状态的任务

上面两种语法没啥特殊的等于以下写法

javascript 复制代码
const promise = new Promise((resolve,reject)=>{
    resolve("成功")
})
const promise = new Promise((resolve,reject)=>{
    reject("失败")
})

Promise.all(任务数组) 直接返回一个任务,任务数组全部成功则成功,任何一个失败则失败

Promise.any(任务数组) 返回一个任务,任务数组任一成功则成功,任务全部失败则失败

代码理解

javascript 复制代码
 const promise = Promise.all([Promise.resolve("成功"), Promise.reject("失败")]);
 //这是一个失败状态
 promise.then(null, (error) => {
   console.log(error);
 });

const promise = Promise.any([Promise.resolve("成功"), Promise.reject("失败")]);
 //这是一个成功的状态
 promise.then(()=>{
     console.log("成功了")
 });

Promise.allSettled(任务数组) 返回一个任务,任务数组全部已决则成功,该任务不会失败

只有状态都从pending状态改变才会执行then。

javascript 复制代码
  const pro1 = () =>
   new Promise((resolve, reject) => {
     setTimeout(() => {
       resolve("成功了1");
     }, 2000);
   });
 const pro2 = () =>
   new Promise((resolve, reject) => {
     setTimeout(() => {
       resolve("失败了1");
     }, 4000);
   });
 const promise = Promise.allSettled([pro1(), pro2()]);
 //这是一个失败状态
 promise.then(
   (data) => {
     console.log(data, "成功了");
   },
   (error) => {
     console.log(error);
   }
 );

Promise.race(任务数组) 返回一个任务,任务数组任一已决则已决,状态和其一致

只要状态有一个从pending状态改变就会执行then。

javascript 复制代码
  const pro1 = () =>
   new Promise((resolve, reject) => {
     setTimeout(() => {
       resolve("成功了1");
     }, 2000);
   });
 const pro2 = () =>
   new Promise((resolve, reject) => {
     setTimeout(() => {
       resolve("失败了1");
     }, 4000);
   });
 const promise = Promise.race([pro1(), pro2()]);
 //这是一个失败状态
 promise.then(
   (data) => {
     console.log(data, "成功了");
   },
   (error) => {
     console.log(error);
   }
 );

理解了Promise静态方法,allSettled最适合上面案例的使用场景

小明做家务,进行代码优化

javascript 复制代码
const wash = () => {
   return new Promise((resolve, rejected) => {
     console.log("小明打开了洗衣机");
     setTimeout(() => {
       if (Math.random() < 0.5) {
         resolve("衣服洗好了");
       } else {
         rejected("忘记加水了,把衣服整烂了");
       }
     }, 1000);
   });
 };
 const sweep = () => {
   return new Promise((resolve, rejected) => {
     console.log("小明打开了扫地机器人");
     setTimeout(() => {
       if (Math.random() < 0.5) {
         resolve("地扫好了");
       } else {
         rejected("机器人坏了,没有扫地");
       }
     }, 2000);
   });
 };
 Promise.allSettled([wash(), sweep()]).then((data) => {
   console.log(data);
 });

由于promise通过链式调用消除了回调地狱问题

官方就推出了新的关键字,更加优雅的表达promise

6、回调消除 Async 和 Await

async

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

javascript 复制代码
 const method1 = async () => {
    return 1; //该函数的返回值是Promise完成后的数据
  };
  method1(); //Promise {1}
  const method2 = async () => {
    return Promise.resolve(1); //若返回的是Promise,则method得到的Promise状态和其一致,简单理解就是等于method2去掉async关键字
  };
  method2(); //Promise {1}
  const method3 = async () => {
    throw new Error("1"); //若执行过程报错,则任务是rejected
  };
  method3(); //Promise{<rejected> Error(1)}

await

await 关键字表示等待某个Promise完成,它必须用于async函数中

javascript 复制代码
  async function method() {
   const n = await Promise.resolve(1);
   console.log(n);
 }
 //上面的函数等于
 function method1() {
   return new Promise((resolve, reject) => {
     Promise.resolve(1).then((n) => {
       console.log(n);
       resolve(1);
     });
   });
 }

注意

await必须在async函数中使用

以上都是有关于Primise的所有知识了。

7、手写Promise

在手写promise之前,先简单介绍一下为什么要手写promise

目前针对浏览器,Promise的支持是非常好的,即便是有些浏览器不支持Promise,也可以通过工程化做兼容处理,平时开发中不需要手写。但是在一些大厂面试时考察评率很高。

不仅是面试需要,作为一名高级工程师,还需要一个强的逻辑思维能力,把想的变成代码实现。

本篇文章手写Promise,主要的就是实现Promise A+规范

说明

本篇文章不会百分之百还原Promise A+规范,但是会还原它核心的百分之八九十

实现状态的变化

由于Promise是一个构造函数,所以选用es6中calss来实现

typescript 复制代码
 //原始Promise
  const promise = new Promise((resolve, reject) => {});
  
  const PENDING = "pending";
  const FULFILLED = "fulfilled";
  const REJECTED = "rejected";
  class MyPromise {
    state: string;
    value: undefined;
    /**
     * 创建Promise
     * @param {Function} executor 任务的执行器,立即执行  === (resolve, reject) => {}
     */

    constructor(executor: any) {
      this.state = PENDING;
      this.value = undefined;
      //函数执行错误,promise直接变成reject状态
      try {
        executor(this.#resolve.bind(this), this.#reject.bind(this));
      } catch (error) {
        this.#reject(error);
      }
    }
    #changeState(newState: string, value: any) {
      //由于promise的状态不能随便更改,加一个判断
      if (this.state !== PENDING) return;
      this.state = newState;
      this.value = value;
    }
    /**
     * 标记当前任务完成
     * @param {any} data 任务完成时的相关参数
     */
    #resolve(data: any) {
      //改变状态和数据
      this.#changeState(FULFILLED, data);
    }
    /**
     * 标记当前任务失败
     *  @param {any} reason 任务失败的相关数据
     */
    #reject(reason: any) {
      //改变状态和数据

      this.#changeState(REJECTED, reason);
    }
  }
  const myPromise = new MyPromise((resolve, reject) => {
    resolve("成功数据");
    reject("失败数据");
  });
  console.log(myPromise);

实现then函数

思考then函数的特点

  • 接收两个参数 onFulfilled和onRejected

  • 函数返回一个新的Promise

  • then函数中onFulfilled和onRejected执行时机。在微任务队列中执行

代码实现

typescript 复制代码
//实现一个微任务队列
  /**
   * 把传递的函数放在微队列中
   * @param {Function} callback 回掉函数
   */
  const runMicroTask = (callback: any) => {
    //判断node环境,nextTick就是node环境中的微队列api
    if (!!process?.nextTick) {
      process.nextTick(callback);
    }
    //浏览器环境通过 MutationObserver 来加入微队列  MutationObserver可以异步监听dom结点的变化
    else if (MutationObserver) {
      const p = document.createElement("p");
      const observer = new MutationObserver(callback);
      observer.observe(p, { childList: true });
      p.innerHTML = "promise mock";
    } else {
      setTimeout(callback);
    }
  };

//新增then方法

  then(onFulfilled: any, onRejected: any) {
      return new MyPromise((resolve: any, reject: any) => {});
    }

执行队列

分析Promise中then函数的执行时机,发现then函数并不会马上执行,甚至都不会马上放到微队列。它一定是等状态确定后才会执行

then作用实际上是把传入的函数参数放入一个队列,then函数每调一次就把callback放入任务队列中。

当状态改变之后,再从队列中取出callback,依次执行一遍

graph TD callback1 --> 任务队列 callback2 --> 任务队列 callback3 --> 任务队列 ... --> 任务队列

代码实现

typescript 复制代码
//新增 任务队列
 #handlers: { executor: any; state: string; resolve: any; reject: any }[] = [];
 
 //加入任务队列函数
 #pushHandler(executor: any, state: string, resolve: any, reject: any) {
      this.#handlers.push({ executor, state, resolve, reject });
    }
 
 then(onFulfilled: any, onRejected: any) {
      return new MyPromise((resolve: any, reject: any) => {
        this.#pushHandler(onFulfilled, FULFILLED, resolve, reject);
        this.#pushHandler(onRejected, REJECTED, resolve, reject);
      });
    }

遍历执行队列

  • 遍历任务队列
kotlin 复制代码
    /**
     * 根据实际情况执行队列,再状态改变的地方执行
     */
    #runHandlers() {
      if (this.state === PENDING) {
        //目前任务任然挂起
        return;
      }
      while (this.#handlers[0]) {
        this.#runOneHandler(this.#handlers[0]);
        this.#handlers.shift();
        //任务必须处理一个删掉一个,不然异步任务会出现多次处理情况
      }
    }
    /**
     *
     * @param {Object} handler
     */
    #runOneHandler(handler: any) {}
  • 确定任务执行时机
typescript 复制代码
  then(onFulfilled: any, onRejected: any) {
     return new MyPromise((resolve: any, reject: any) => {
       this.#pushHandler(onFulfilled, FULFILLED, resolve, reject);
       this.#pushHandler(onRejected, REJECTED, resolve, reject);
       this.#runHandlers(); //执行then函数调用之前就改变的状态的任务
     });
   }
   
     #changeState(newState: string, value: any) {
     //由于promise的状态不能随便更改,加一个判断
     if (this.state !== PENDING) return;
     this.state = newState;
     this.value = value;
     this.#runHandlers(); //状态变化执行队列
   }

完成核心代码运行处理函数

直接上代码

typescript 复制代码
 const isPromise = (obj: Record<string, any>) => {
   return !!obj && typeof obj === "object" && typeof obj.then === "function";
 };
 
 /**
    *
    * @param {Object} handler
    */
   #runOneHandler({ executor, state, resolve, reject }: any) {
     runMicroTask(() => {
       if (this.state !== state) return; //状态不一致,不做任何处理
       if (typeof executor !== "function") {
         //如果传入的不是一个函数
         //这里不太好理解,当传入的executor没有处理函数的时候,下一次的promise要和上一次的promise状态保持完全一致
         this.state === FULFILLED ? resolve(this.value) : reject(this.value);
         return;
       }
       //当前任务状态一致且executor是一个函数
       try {
         const result = executor(this.value);
         if (isPromise(result)) {
           result.then(resolve, reject);
         } else {
           resolve(result);
         }
       } catch (error) {
         reject(error);
       }
     });
   }

最后完整代码

kotlin 复制代码
  //原始Promise
 const promise = new Promise((resolve, reject) => {}).then(
   (data) => {},
   (reason) => {}
 );

 const PENDING = "pending";
 const FULFILLED = "fulfilled";
 const REJECTED = "rejected";

 const isPromise = (obj: Record<string, any>) => {
   return !!obj && typeof obj === "object" && typeof obj.then === "function";
 };

 /**
  * 把传递的函数放在微队列中
  * @param {Function} callback 回掉函数
  */
 const runMicroTask = (callback: any) => {
   //判断node环境,nextTick就是node环境中的微队列api
   if (!!process?.nextTick) {
     process.nextTick(callback);
   }
   //浏览器环境通过 MutationObserver 来加入微队列  MutationObserver可以异步监听dom结点的变化
   else if (MutationObserver) {
     const p = document.createElement("p");
     const observer = new MutationObserver(callback);
     observer.observe(p, { childList: true });
     p.innerHTML = "promise mock";
   } else {
     setTimeout(callback);
   }
 };
 class MyPromise {
   state: string;
   value: undefined;
   #handlers: { executor: any; state: string; resolve: any; reject: any }[] = [];
   // 处理函数存放的队列,数据类型必须是一个对象,不然没法区分失败和成功的处理函数
   //参数说明 executor:then的回掉函数,state:回掉函数中data和reason处理状态标识
   //resolve data处理函数 reject:reason处理函数

   /**
    * 创建Promise
    * @param {Function} executor 任务的执行器,立即执行  === (resolve, reject) => {}
    */

   constructor(executor: any) {
     this.state = PENDING;
     this.value = undefined;
     //函数执行错误,promise直接变成reject状态
     try {
       executor(this.#resolve.bind(this), this.#reject.bind(this));
     } catch (error) {
       this.#reject(error);
     }
   }
   #changeState(newState: string, value: any) {
     //由于promise的状态不能随便更改,加一个判断
     if (this.state !== PENDING) return;
     this.state = newState;
     this.value = value;
     this.#runHandlers(); //状态变化执行队列
   }
   /**
    *
    * @param executor 处理函数
    * @param state   //处理函数执行状态
    */
   #pushHandler(executor: any, state: string, resolve: any, reject: any) {
     this.#handlers.push({ executor, state, resolve, reject });
   }

   /**
    * 根据实际情况执行队列,再状态改变的地方执行
    */
   #runHandlers() {
     if (this.state === PENDING) {
       //目前任务任然挂起
       return;
     }
     while (this.#handlers[0]) {
       this.#runOneHandler(this.#handlers[0]);
       this.#handlers.shift();
       //任务必须处理一个删掉一个,不然异步任务会出现多次处理情况
     }
   }
   /**
    *
    * @param {Object} handler
    */
   #runOneHandler({ executor, state, resolve, reject }: any) {
     runMicroTask(() => {
       if (this.state !== state) return; //状态不一致,不做任何处理
       if (typeof executor !== "function") {
         //如果传入的不是一个函数
         //这里不太好理解,当传入的executor没有处理函数的时候,下一次的promise要和上一次的promise状态保持完全一致
         this.state === FULFILLED ? resolve(this.value) : reject(this.value);
         return;
       }
       //当前任务状态一致且executor是一个函数
       try {
         const result = executor(this.value);
         if (isPromise(result)) {
           result.then(resolve, reject);
         } else {
           resolve(result);
         }
       } catch (error) {
         reject(error);
       }
     });
   }

   then(onFulfilled: any, onRejected: any) {
     return new MyPromise((resolve: any, reject: any) => {
       this.#pushHandler(onFulfilled, FULFILLED, resolve, reject);
       this.#pushHandler(onRejected, REJECTED, resolve, reject);
       this.#runHandlers(); //执行then函数调用之前就改变的状态的任务
     });
   }
   /**
    * 标记当前任务完成
    * @param {any} data 任务完成时的相关参数
    */
   #resolve(data: any) {
     //改变状态和数据
     this.#changeState(FULFILLED, data);
   }
   /**
    * 标记当前任务失败
    *  @param {any} reason 任务失败的相关数据
    */
   #reject(reason: any) {
     //改变状态和数据

     this.#changeState(REJECTED, reason);
   }
 }
 const myPromise = new MyPromise((resolve, reject) => {
   resolve("成功数据");
   reject("失败数据");
 });
 console.log(myPromise);
相关推荐
Codelinghu11 分钟前
做后端的我在公司造了一个前端轮子,领导:嘿!你他娘的真是个天才。
前端
小old弟16 分钟前
vue3模板中ref的实现原理
前端·vue.js
招风的黑耳20 分钟前
ElementUI元件库——提升Axure原型设计效率与质量
前端·elementui·axure
Captaincc23 分钟前
用MCP 让Claude控制ChatGPT 4o,自动生成吉卜力风格的分镜
前端·claude·mcp
阿白的白日梦29 分钟前
JSX 元素隐式具有类型 "any",因为不存在接口 "JSX.IntrinsicElements"。ts 无法使用 JSX,除非提供了 "--js
前端·javascript·typescript
amagi60029 分钟前
关于黑马程序员微信小程序案例3-3的静态配置问题
前端
curdcv_po33 分钟前
React 进阶:useReducer 详解与实战指南
前端
用户40993225021238 分钟前
深入掌握FastAPI与OpenAPI规范的高级适配技巧
前端·javascript·后端
东东__net38 分钟前
1_vue基本_插件
java·前端·javascript
curdcv_po1 小时前
聊一聊js的全局作用域和局部作用域
前端