Promise分享

news2024/11/27 18:40:12

手写promise之前需要知道

  1. 宏任务 & 微任务
    我们都知道 Js 是单线程的,但是一些高耗时操作就带来了进程阻塞问题。为了解决这个问题,Js 有两种任务的执行模式:同步模式(Synchronous)和异步模式(Asynchronous)。

JS异步的实现靠的就是浏览器的多线程,当他遇到异步API时,就将这个任务交给对应的线程,当这个异步API满足回调条件时,对应的线程又通过事件触发线程将这个事件放入任务队列,然后主线程从任务队列取出事件继续执行。以浏览器为例,有以下几个常见的进程和线程:
在这里插入图片描述

在异步模式下,创建异步任务主要分为宏任务与微任务两种。ES6 规范中,宏任务(Macrotask) 称为 Task, 微任务(Microtask) 称为 Jobs。宏任务是由宿主(浏览器、Node)发起的,而微任务由 JS 自身发起。
宏任务与微任务的几种创建方式 👇

宏任务(Macrotask)微任务(Microtask)
setTimeoutrequestAnimationFrame(有争议)
setIntervalMutationObserver(浏览器环境)
MessageChannelPromise.[ then/catch/finally ]
I/O,事件队列process.nextTick(Node环境)
setImmediate(Node环境)queueMicrotask
script(整体代码块)

来道面试题:

async function async1 () {
    console.log('async1 start');
    await async2();
    console.log('async end')
}
async function async2 () {
    console.log('async2')
}
console.log('script start')
setTimeout(() => {
    console.log('serTimeout')
}, 0)
async1()
new Promise((function (resolve) {
    console.log('promise1')
    resolve()
})).then(function () {
    console.log('promise2')
}).then(function () {
    console.log('promise3')
}).then(function () {
    console.log('promise4')
})
console.log('script end')

queueMicrotask的用法

console.log('script start');
new Promise((resolve) => {
    console.log('pr start');
    resolve();
}).then(res => {
    console.log('pr then');
}) .then(res => {
    console.log('pr end');
})
queueMicrotask(() => {
    console.log('queueMicrotask');
})
console.log('script end');
  1. 什么是Promise A+规范
    当别人问起来什么是Promise/A+规范,可能会觉得有点懵,你可能用过Promise,但很可能不了解什么是Promise规范。
    其实Promise 规范有很多,如Promise/A,Promise/B,Promise/D 以及 Promise/A 的升级版 Promise/A+。ES6中采用了 Promise/A+ 规范。
  2. Promise标准解读
    1. 一个promise的当前状态只能是pending、fulfilled和rejected三种之一。状态改变只能是pending到fulfilled或者pending到rejected。状态改变不可逆。
    2. promise的then方法接收两个可选参数,表示该promise状态改变时的回调(promise.then(onFulfilled, onRejected))。then方法返回一个promise,then 方法可以被同一个 promise 调用多次。
    3. Promise/A+并未规范race、all、catch方法,这些是ES6自己规范的。

正式开始

  1. 确定一个异步方法
微任务(Microtask)
requestAnimationFrame(有争议)
MutationObserver(浏览器环境)
Promise.[ then/catch/finally ]
process.nextTick(Node环境)
queueMicrotask

在这里插入图片描述

其中不需要判断环境的,也就queueMicrotask了,但是queueMicrotask的兼容性不是很好,所以写一个兼容方法:

const asyncFn = function () {
    if (typeof queueMicrotask === 'function') {
        return queueMicrotask;
    }
    if (typeof process === 'object' && process !== null && typeof (process.nextTick) === 'function') {
        return process.nextTick;
    }
    if (typeof (setImmediate) === 'function') {
        return setImmediate
    }
    return setTimeout
}()
  1. 写一个基础的Promise
const pr = new Promise((resolve, reject) => {
    const number = Math.random();
    if (number > 0.5) {
        resolve(number);
    } else {
        reject(new Error(number));
    }
});

const STATUS_PADDING = Symbol('PADDING');
const STATUS_FULFILLED = Symbol('FULFILLED');
const STATUS_REJECTED = Symbol('REJECTED');

class MiniPromise {
    constructor(callback){
        try {        
            callback(this.onFulfilled.bind(this), this.onRejected.bind(this));
        } catch (error) {
            this.onRejected(error);
        }
    }
    value = null;
    status = STATUS_PADDING;
    
    onFulfilled(value) {
        this.status = STATUS_FULFILLED;
        this.value = value;
    }
    onRejected(error) {
        this.status = STATUS_REJECTED;
        this.value = error;
    }
}
  1. 实现then
pr.then(res => {
    console.log('success::', res);
}, err => {
    console.error('error:::', err);
});

// 调用then中的方法时,需要判断当前promise的状态
// 如果是padding状态,需要等待promise的状态更新为fulfilled/rejected时才能调用
// 所以需要保存一下callback,等更新后再调用这个方法。
then(resolve, reject) {
    // 平常使用的时候,一般只传递了第一个参数,没有传reject方法,所以给他们加上默认值
    resolve = typeof resolve === 'function' ? resolve : value => value;
    reject = typeof reject === 'function' ? reject : value => value;
    
    const newPromise = new MiniPromise(() => {});
    switch(this.status) {
        case STATUS_PADDING:
            this.resolveCallback = resolve;
            this.rejectCallback = reject;
            this.thenPromise = newPromise;
            break;
        case STATUS_FULFILLED:
            try {
                newPromise.onFulfilled(resolve(this.value));
            } catch(err) {
                this.status = STATUS_REJECTED;
                newPromise.onRejected(reject(err));
            }
            break;
        case STATUS_REJCETED:
            try {
                newPromise.onRejected(reject(this.value));
            } catch(err) {
                newPromise.onRejected(reject(err));
            }
            break;
    }
    return newPromise;
}
  1. 多个then调用
const pr2 = pr.then(res => {
    console.log('success:222:', res);
}, err => {
    console.error('error:222:', err);
});
const pr3 = pr.then(res => {
    console.log('success:333:', res);
}, err => {
    console.error('error:333:', err);
});

想想改咋改?

// 将this.rejectCallback改为数组就好了
 onFulfilled(value) {
    this.status = STATUS_FULFILLED;
    this.value = value;
    let index = 0;
    while(this.resolveCallback.length) {
        const item = this. resolveCallback.shift();
        try {
            const result = item(this.value);
            this.thenPromiseList[index]. onFulfilled(result);
        } catch(err) {
            console.log('xxx resolve:::::', err);
            this.thenPromiseList[index].onRejected(err);
        }
        index ++;
    }
}
onRejected(error) {
    this.status = STATUS_REJECTED;
    this.value = error;
    let index = 0;
    while(this.rejectCallback.length) {
        const item = this.rejectCallback.shift();
        try {
            const result = item(this.value);
            this.thenPromiseList[index].onRejected(result);
        } catch(err) {
            console.log('xxx resolve:::', err);
            this.thenPromiseList[index].onRejected(err);
        }
        index ++;
    }
}

then(resolve, reject) {
    // ......
    switch(this.status) {
        case STATUS_PADDING:
            this.resolveCallback = resolve;
            this.rejectCallback = reject;
            this.resolveCallback.push(resolve);
            this.rejectCallback.push(reject);
            this.thenPromiseList.push(newPromise)
            break;
        // .....
    }
}
  1. 实现错误catch
catch(onRejected) {
   if (typeof onRejected !== 'function') {
       onRejected = reason => reason;
   }
   const newPromise = new MiniPromise(() => {});
   switch(this.status) {
       case STATUS_REJECTED:
           newPromise.status = this.status;
           newPromise.value = onRejected(this.value);
           break;
       case STATUS_PADDING:
           this.rejectCallbackList.push(onRejected);
           this.thenPromiseList.push(newPromise);
           break;
   }
   return newPromise;
}
  1. 实现直接调用resolve和reject
// 一般来说,写在class中只有属性和方法.都可以被实例化的对象进行调用,比如:
class Animal {
    type = '动物'
    name = 'animal'
}
const dog = new Animal();
dog.type // 动物
dog.name // animal
// 假如想要在外部直接使用class内部的方法,或属性,比如这样:
class Person {
    name = "shen"
}
console.log(Person.name); // 这样会打印Person1【类的名称】,并取不到shen

// 这时就需要使用static关键字
class Person {
    static name = 'shen';
}
console.log(Person.name);    // 这样就可以得到 shen 了
// 静态的方法中也可以使用同一个类下的其他静态属性或方法
class Person2 {
    static hhha = "aaa"
    static sayHi() {
        console.log(this.hhha + ' hi~');
    }
}
Person2.sayHi() // aaa hi~
// 如果hhha 不是static的,那么sayHi会打印出 undefined hi~

// react中的getDerivedStateFromProps,也是一个静态方法

回归正题,想要实现直接调用resolve,和reject方法,也需要使用static关键字

// 先看看使用方法
const resPr = Promise.resolve(123);

class MiniPromise {
    // ...
    static resolve(value) {
        const newPr = new MiniPromise((resolve, reject) => {
            try {
                resolve(value);
            } catch(err) {
                reject(err);
            }
        })
        return newPr;
    }    
    static reject(value) {
        const newPr = new MiniPromise((resolve, reject) => {
            try {
                reject(value);
            } catch(err) {
                reject(err);
            }
        })
        return newPr;
    }
    // ...
}

整体看一下代码

class MiniPromise {
    constructor(callback) {
        try {
            callback(this.handleFulfilled.bind(this), this.handleRejected.bind(this));
        } catch(err) {
            this.handleRejected(err);
        }
    }
    status = STATUS_PADDING;
    value = undefined;
    rejectCallbackList = [];
    fulfillCallbackList = [];

    thenPromiseList = [];

    static resolve(value) {
        const newPromise = new MiniPromise((resolve, rejcet) => {
            try {
                console.log(value);
                resolve(value);
            } catch(err) {
                rejcet(err);
            }
        });
        return newPromise;
    }
    static reject(value) {
        const newPromise = new MiniPromise((resolve, reject) => {
            try {
                reject(value);
            } catch(err) {
                reject(err);
            }
        });
        return newPromise;
    }

    handleFulfilled(value) {
        this.status = STATUS_FULFILLED;
        this.value = value;
        console.log('resolve', value, this.fulfillCallbackList);
        let index = 0;
        while(this.fulfillCallbackList.length) {
            const item = this.fulfillCallbackList.shift();
            try {
                const result = item(this.value);
                if (result === this.thenPromiseList[index]) {
                    this.thenPromiseList[index].handleRejected(new TypeError('Chaining cycle detected for promise #<Promise>'));
                    continue
                };
                this.thenPromiseList[index].handleFulfilled(result);
            } catch(err) {
                console.log('xxx resolve:::::', err);
                this.thenPromiseList[index].handleRejected(err);
            }
            index ++;
        }
    }
    handleRejected(reason) {
        this.status = STATUS_REJECTED;
        this.value = reason;
        console.log('rejcet', reason);
        let index = 0
        while(this.rejectCallbackList.length) {
            const item = this.rejectCallbackList.shift();
            try {
                const result = item(this.value);
                if (result === this.thenPromiseList[index]) {
                    this.thenPromiseList[index].handleRejected(new TypeError('Chaining cycle detected for promise #<Promise>'))
                    continue
                };

                this.thenPromiseList[index].handleRejected(result);
            } catch(err) {
                this.thenPromiseList[index].handleRejected(err);
            }
            index ++;
        }
    }
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason => reason;
        const newPromise = new MiniPromise(() => {});
        switch(this.status) {
            case STATUS_FULFILLED:
                try {
                    newPromise.status = this.status;
                    newPromise.value = onFulfilled(this.value);
                } catch(err) {
                    console.log('errr', err);
                    newPromise.value = onRejected(err);
                }
                break;
            case STATUS_REJECTED:
                newPromise.status = this.status;
                try {
                    newPromise.value = onRejected(this.value);
                } catch(err) {
                    newPromise.value = onRejected(err);
                }
                break;
            case STATUS_PADDING:
                this.rejectCallbackList.push(onRejected);
                this.fulfillCallbackList.push(onFulfilled);
                this.thenPromiseList.push(newPromise);
                break;
        }
        return newPromise;
    }

    catch(onRejected) {
        if (typeof onRejected !== 'function') {
            onRejected = reason => reason;
        }
        const newPromise = new MiniPromise(() => {});
        switch(this.status) {
            case STATUS_REJECTED:
                newPromise.status = this.status;
                newPromise.value = onRejected(this.value);
                break;
            case STATUS_PADDING:
                this.rejectCallbackList.push(onRejected);
                this.thenPromiseList.push(newPromise);
                break;
        }
        return newPromise;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/753127.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年先进封装行业研究报告

第一章 行业概况 1.1 概述 封装是半导体制造过程中的一个重要步骤。在这个步骤中&#xff0c;半导体芯片&#xff08;或称为集成电路&#xff09;被包裹在一个保护性的外壳中。这个外壳的主要功能是保护芯片免受物理和化学损害&#xff0c;例如防止芯片受到潮湿、尘埃、温度变…

git clone 或者是vscode clone 时遇到the remote end hung up unexpectedly

fatal: the remote end hung up unexpectedly fatal: early EOF fatal: index-pack failed使用git clone总是报错 查看原因有三种可能&#xff1a;要么是缓存不够&#xff0c;要么是网络不行&#xff0c;要么墙的原因。 如果是网络不行&#xff0c;可以配置git的最低速度和最…

pdf.js移动端展示预览打开pdf-pdfh5.js

有问题可以加Q群咨询&#xff0c;技术交流群&#xff0c;也可以探讨技术&#xff0c;另有微信群可以问群主拉入微信群 QQ群521681398 pdfh5博客主页 pdfh5项目GitHub地址 pdfh5项目gitee地址 react、vue均可使用 example/test是vue使用示例 example/vue3demo是vue3使用示…

第三章系统控制(Cortex-M7 Processor)

第三章系统控制 目录 第三章系统控制 3.1关于系统控制 3.2寄存器汇总 3.3寄存器描述 3.3.1辅助控制寄存器 3.3.2cpu基寄存器 3.3.3 Cache Level ID寄存器 3.3.4缓存大小ID寄存器 3.3.5缓存大小选择寄存器 3.3.6指令和数据紧密耦合的存储器控制寄存器 3.3.7 AHBP控制寄存器 3…

2023京东防暑消暑市场分析:冷风扇、移动空调等硬核防暑产品火爆

今年夏天&#xff0c;高温天气频发&#xff0c;各种防暑类产品也向多场景延伸&#xff0c;不少行业、类目都因此高速增长&#xff0c;包括防暑类电器、防晒用品、小型户外避暑神器等。 *高温刺激下&#xff0c;防暑类家电需求暴涨 在夏季高温的刺激下&#xff0c;空调、风扇等等…

HTML input text 常用事件

前言 用于记录开发中常用到的&#xff0c;快捷开发 简单实例 <input type"text" name"noSecretKeyJson" maxlength"200" />常用事件 oninput &#xff08;在用户输入时触发&#xff09;及案例 案例一&#xff1a;限制只允许输入数字…

Java 函数式编程(常用接口)

之前已经介绍过了Java8函数式变成及Lambda表达式&#xff0c;感兴趣可以看看&#xff0c;地址&#xff1a;Java8函数式编程&#xff08;Lambda表达式&#xff09;_琅琊之榜PJ的博客-CSDN博客 本文主要介绍一下常用的接口及用法&#xff0c;先来看一个表格&#xff1a; 本文主要…

强引用、软引用、弱引用和虚引用的区别

主要的区别在于什么时候回收对象&#xff1b; 强引用&#xff1a;垃圾回收器就不会回收这个对象&#xff1b;软引用&#xff1a;如果内存足够&#xff0c;不回收&#xff0c;如果内存不足&#xff0c;则回收&#xff1b;弱引用&#xff1a;不管当前内存空间足够与否&#xff0…

Jupyter notebook添加与删除kernel

目录 1 添加虚拟环境的kernel 2 删除jupyter notebook已有的kernal 3 切换内核与查看当前内核 4 添加C语言的kernel 5 添加python2的kernel 6 添加java语言的kernel 6.1 sudo apt install default-jre 6.2 下载并安装 ijava 6.3 sudo apt install openjdk-11…

四、评估已建立的模型

别人不讲道理 不是我们跟着不讲道理的理由 1 模型评估 希望fθ(x)对未知数据x输出的预测值尽可能正确。 如何测量预测函数fθ(x)的正确性&#xff0c;也就是精度呢&#xff1f; 观察函数的图形&#xff0c;看它能否很好地拟合训练数据&#xff1a; 我们需要能够定量地表示机…

2022 JavaScript调查:TypeScript持续主导,Vite和Tauri大受欢迎

StateOfJS 最新发布了 2022 年 JavaScript 现状调查报告指出&#xff0c;Solid 和 Qwik 等新兴前端框架正在挑战 React 的权威。该报告基于对近 40,000 名 Web 开发人员的调查&#xff0c;数量几乎是去年的两倍。 JavaScript 可能发展得很快&#xff0c;但 JavaScript 开发人员…

4. Shuffle 5. 内存的管理

4. Shuffle (1) Shuffle 的原理和执行过程 在Scala中&#xff0c;Shuffle是指对集合或序列进行随机打乱或重新排列的操作。它可以用于打乱集合中元素的顺序&#xff0c;以便在后续的操作中获得更好的随机性或均匀性。 在Scala中&#xff0c;可以使用scala.util.Random类的shu…

只会Excel制表?搭配上这个工具,让你的办公效率无限翻倍

在每次考试之后&#xff0c;老师们通常需要进行繁重的工作&#xff0c;包括使用Excel汇总学生的考试成绩、计算平均分和成绩排名等。然而&#xff0c;现在学校不允许公布学生成绩&#xff0c;这给老师们增加了额外的工作量和麻烦。一些老师采取了将学生成绩表截成一条一条的成绩…

使用HummerRisk进行K8s安全合规检测

1.简介 HummerRisk 是开源的云原生安全平台&#xff0c;以非侵入的方式解决云原生的安全和治理问题。核心能力包括混合云的安全治理和云原生安全检测。 今天我们来通过 HummerRisk 云原生安全检测能力来对Kubernetes进行安全合规检测 2.检测步骤 ①首先创建一个Kubernetes账…

spring复习:(19)单例bean是在哪里被加入到一级缓存的?

AbstractBeanFactory的doGetBean方法&#xff1a; 如果是第一次调用getBean时&#xff0c;会执行到下边的代码&#xff1a; getSingleton的代码如下&#xff1a; public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(bea…

windows安装mysql8.0.23版本成功示例-免安装

windows安装mysql8.0.23版本成功示例 一.通过mysql-installer-*.msi安装包的方式1.安装准备1.1 官网下载地址1.2 选择合适的版本点击下载 2.安装mysql 二.通过mysql-8.0.23-winx64.zip压缩包免安装方式1.安装准备1.1 下载官网压缩包1.2 解压后配置文件my.ini信息1.3 配置my.ini…

共筑开源新长城 龙蜥社区走进开放原子校源行-清华大学站

6 月 28 日&#xff0c;以“聚缘于校&#xff0c;开源共行”为主题的 2023 年开放原子校源行活动在清华大学成功举行。本次活动由开放原子开源基金会和清华大学共同主办&#xff0c;来自各行业的 22 位大咖共聚校园共话开源。龙蜥社区技术专家边子政受邀进行技术分享&#xff0…

安达发|如何选择性价比高的APS软件?

生产计划和调度决不是越精确越好。理论上&#xff0c;APS可以调度每一分钟&#xff0c;可以调度每一个人&#xff0c;每一台设备的每一个动作。这只对自动化生产线有意义。 我国企业管理水平参差不齐。许多工业企业可能不像一百年前的泰勒时代那样管理得那么好。对于一些工业企…

搭载率突破40%!智能数字座舱比拼,车企还有降本空间吗?

进入2023年&#xff0c;汽车行业的「降本」风潮&#xff0c;驱动产业链上下游开始思考智能化、电动化的投入产出。除了显性的硬件成本&#xff08;继续堆料&#xff0c;强调性价比&#xff0c;还是减配&#xff09;&#xff0c;软件及背后的开发成本&#xff0c;对于车企来说&a…

C++STL:关联容器之map和multimap

文章目录 1. map概述成员函数创建C map容器的几种方法迭代器map获取键对应值的几种方法map insert()插入数据的4种方式map emplace()和emplace_hint()方法 2. multimap概述成员函数创建C multimap容器的方法 1. map 概述 作为关联式容器的一种&#xff0c;map 容器存储的都是…