手写promis(1)---核心功能篇

目录

前言

核心功能--构造函数 

核心功能--状态及原因

then方法

成功和失败回调

异步及多次调用

异步任务--核心api

Promise.then:

queueMicrotask:

MutationObserver:

setImmediate:

setTimeout:

异步任务---函数封装


前言

Promise(承诺)是一种用于处理异步操作的JavaScript编程技术。它是一种代表一个尚未完成的操作的对象,该操作可能会在将来的某个时刻完成或失败。Promise提供了一种更结构化和可维护的方式来处理异步代码,特别是在处理网络请求、文件I/O、定时器等操作时非常有用。

Promise对象有三种状态:

  1. Pending(进行中):初始状态,表示操作尚未完成或失败。
  2. Fulfilled(已完成):表示操作成功完成。
  3. Rejected(已失败):表示操作失败。

Promise对象具有两个重要的方法:

  1. then(): 用于注册回调函数,当Promise状态变为Fulfilled时执行。
  2. catch(): 用于注册回调函数,当Promise状态变为Rejected时执行。

使用Promise,可以更清晰地定义异步操作的工作流程,避免了回调地狱(Callback Hell)的问题,使代码更易于理解和维护。以下是一个简单的Promise示例:

const myPromise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    const randomValue = Math.random();
    if (randomValue > 0.5) {
      resolve("操作成功");
    } else {
      reject("操作失败");
    }
  }, 1000);
});

myPromise
  .then(result => {
    console.log(result); // 在操作成功时执行
  })
  .catch(error => {
    console.error(error); // 在操作失败时执行
  });

这个示例创建了一个Promise对象,模拟了一个异步操作,并根据随机值决定是成功还是失败。通过.then().catch()方法,我们可以分别处理成功和失败的情况。这使得异步代码的处理更加结构化和可控。

手写原来步骤

核心功能--构造函数 

  1. 定义类(Define the class): 这里定义了一个名为MyPromise的类,用于创建自定义Promise对象。

  2. 添加构造函数(Add a constructor): 在构造函数中,通过constructor方法定义了一个新的MyPromise对象。当创建MyPromise对象时,会执行构造函数。构造函数接受一个函数func作为参数,这个函数在创建MyPromise对象时传入。

  3. 定义resolve/reject(Define resolve and reject functions): 在构造函数中,定义了resolvereject函数,它们分别用于处理Promise的成功和失败情况。resolve函数用于处理成功情况,它接受一个参数result,表示成功的原因。reject函数用于处理失败情况,它也接受一个参数result,表示失败的原因。

  4. 执行回调函数(Execute the callback function): 最后,构造函数中调用了传入的func函数,这个函数接受resolvereject作为参数。这意味着在创建MyPromise对象时,会执行传入的回调函数,并在适当的时候调用resolvereject函数来处理异步操作的结果

/**
 * 构造函数
 * 1. 定义类
 * 2. 添加构造函数
 * 3. 定义reslove/reject
 * 4. 执行回调函数
 */

//1.定义类
class MyPromise {
    // 2.添加构造函数 new时会执行 constructor 传入回调函数
    constructor(func) {
        // 3.func 接受两个行参 成功回调(成功原因),失败回调(失败原因)
        // resolve:<Function>(result:<any>)  reject:<Function>(result:<any>) 
        const resolve = (result) => {
            console.log('resolve执行了--', result);
        }
        const reject = (result) => {
            console.log('reject执行了--', result);
        }

        // 4. 执行回调函数
        func(resolve, reject)
    }
}


// 测试代码
const p = new MyPromise((resolve, reject) => {
    console.log('执行回调函数');
    // resolve('success')
    reject('error')
})

核心功能--状态及原因

  1. 添加实例属性状态(pending / fulfilled / rejected)(默认为等待)

  2. 添加实例属性原因(默认为undefined)

  3. 调整resolve函数和reject函数 resolvereject函数改变状态和保存原因,这是Promise的核心功能之一。

  4. 添加状态检查:在resolvereject函数中,添加状态检查来确保状态只能从pending改变一次。这可以防止状态被多次改变。
/**
 * 状态及原因
 * 1. 添加状态 pending / fulfilled / rejected
 * 2. 添加原因
 * 3. 调整reslove/reject
 * 4. 状态不可逆
 */

//通过变量保存状态,方便使用
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    // 1. 添加实例属性 状态(pending / fulfilled / rejected)(默认为等待)
    state = PENDING
    // 2. 添加实例属性 原因(默认为undefined)
    result = undefined
    constructor(func) {
        // this 箭头函数this-->上级作用域(构造函数)-->  实例 
        // 3. 调整reslove pending-- > fulfilled 添加原因
        const resolve = (result) => {
            // 4. 状态不可逆
            if (this.state == PENDING) {
                this.state = FULFILLED
                this.result = result
            }
        }

        // 3. reject pending-- > rejected 添加原因
        const reject = (result) => {
            if (this.state == PENDING) {
                this.state = REJECTED
                this.result = result
            }
        }

        func(resolve, reject)
    }
}


// 测试代码
const p = new MyPromise((resolve, reject) => {

    resolve('success')
    // reject('error')
})

then方法

成功和失败回调

  1. 添加实例方法(then)
  2. 参数判断(参考文档)

如果是个函数 执行回调函数 

如果 onFulfilled 不是一个函数,则内部会被替换为一个恒等函数((x) => x),它只是简单地将兑现值向前传递。

如果 onRejected 不是一个函数,则内部会被替换为一个抛出器函数((x) => { throw x; }),它会抛出它收到的拒绝原因。

3.判断状态执行成功回调函数or失败回调函数(实例化完成后状态已经确定)

/**
 * 成功和失败回调
 * 1. 添加实例方法
 * 2. 参数判断(参考文档)
 *  2.1. 执行成功回调
 *  2.2. 执行失败回调
 */

//通过变量保存状态,方便使用
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    state = PENDING
    result = undefined
    constructor(func) {
        const resolve = (result) => {
            if (this.state == PENDING) {
                this.state = FULFILLED
                this.result = result
            }
        }

        const reject = (result) => {
            if (this.state == PENDING) {
                this.state = REJECTED
                this.result = result
            }
        }

        func(resolve, reject)
    }
    // 添加实例方法
    then(onFulfilled, onRejected) {
        // 参数判断  如果 onFulfilled 不是一个函数,则内部会被替换为一个恒等函数((x) => x),它只是简单地将兑现值向前传递。
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x
        // 参数判断  如果 onRejected 不是一个函数,则内部会被替换为一个抛出器函数((x) => { throw x; }),它会抛出它收到的拒绝原因。
        onRejected = typeof onRejected === 'function' ? onRejected : (x) => { throw x }

        // 执行成功回调or执行失败回调(状态实例化完成就立即调用了,所以可以执行返回结果--》异步暂时不考虑)
        if (this.state === FULFILLED) {
            onFulfilled(this.result)
        } else if (this.state === REJECTED) {
            onRejected(this.result)
        }

    }
}


// 测试代码
const p = new MyPromise((resolve, reject) => {

    // resolve('success')
    reject('error')
})
// p.then((result) => {
//     console.log('成功回调', result)
// }, err => {
//     console.log('失败回调', err);
// })
p.then((result) => {
    console.log('成功回调', result)
})

异步及多次调用

  1. 定义实例属性,添加#为类内部使用标识
  2. 如果then执行后状态为pending存储异步时成功回调与失败回调
  3. 定时器两秒后 resolve执行 取出成功回调函数放入成功原因

  4. 定时器两秒后 reject执行 取出失败回调函数放入失败原因

/**
 * then-异步及多次调用
 * 1. 定义实例属性
 * 2. 保存回调函数
 * 3. 调用成功回调
 * 4. 调用失败回调
 */

//通过变量保存状态,方便使用
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    state = PENDING
    result = undefined
    // 定义实例属性 #外部不能访问 保存回调
    #handlers = []//[{onFulfilled,onRejected}...]
    constructor(func) {
        const resolve = (result) => {
            if (this.state == PENDING) {
                this.state = FULFILLED
                this.result = result
                // 3.定时器两秒后 resolve执行 取出成功回调函数放入成功原因
                this.#handlers.forEach(({ onFulfilled }) => {
                    onFulfilled(this.result)
                })
            }
        }

        const reject = (result) => {
            if (this.state == PENDING) {
                this.state = REJECTED
                this.result = result
                // 4.定时器两秒后 reject执行 取出失败回调函数放入失败原因
                this.#handlers.forEach(({ onRejected }) => {
                    onRejected(this.result)
                })
            }
        }

        func(resolve, reject)
    }

    then(onFulfilled, onRejected) {

        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x

        onRejected = typeof onRejected === 'function' ? onRejected : (x) => { throw x }

        if (this.state === FULFILLED) {
            onFulfilled(this.result)
        } else if (this.state === REJECTED) {
            onRejected(this.result)
        } else if (this.state === PENDING) {
            // 2. 代表状态还没有发生过改变,保存回调函数
            this.#handlers.push({
                onFulfilled, onRejected
            })
        }

    }
}


// 测试代码
const p = new MyPromise((resolve, reject) => {

    setTimeout(() => {
        // resolve('success')
        reject('error')
    }, 2000);
})
p.then((result) => {
    console.log('成功回调', result)
}, err => {
    console.log('失败回调', err);
})
p.then((result) => {
    console.log('成功回调', result)
})

异步任务--核心api

 * Vue Promise.then, MutationObserver,setImmediate,setTimeout

 * 我们选用 queueMicrotask MutationObserver setTimeout

  • Promise.then,手写promise 不考虑这个
  • queueMicrotask:node11,新式浏览器(不包括ie11)
  • MutationObserver node不支持 ie11支持
  • setImmediate:ie10,11支持 edge:(12-18)支持(不考虑)
  • setTimeout:node,浏览器全支持

Promise.then:

  • 兼容性: 几乎所有现代浏览器和Node.js都支持Promise。
  • 用法: Promise是一种用于处理异步操作的机制,通过.then()方法可以注册回调函数来处理异步任务的完成或失败。例如:
const myPromise = new Promise((resolve, reject) => {
  // 异步操作
  if (/* 操作成功 */) {
    resolve(result);
  } else {
    reject(error);
  }
});

myPromise.then(
  (result) => {
    // 处理成功的情况
  },
  (error) => {
    // 处理失败的情况
  }
);

queueMicrotask:

Window 或 Worker 接口的 queueMicrotask() 方法,将微任务加入队列以在控制返回浏览器的事件循环之前的安全时间执行。

微任务是一个简短的函数,它将在当前任务完成其工作后运行,并且在执行上下文的控制权返回到浏览器的事件循环之前没有其他代码等待运行时运行。

它让你的代码在运行时不会干扰任何可能具有更高优先级的代码的运行,但在浏览器重新获得对执行上下文的控制之前,这可能取决于你需要完成的工作。你可以在我们的微任务指南中了解更多关于如何使用微任务以及选择这样做的原因。

微任务的重要性在于它能够以特定顺序异步执行任务。查看在 JavaScript 中通过 queueMicrotask() 使用微任务的详情。

微任务对于需要执行最后阶段的任务或其他在渲染之前的任务的库和框架特别有用。

参数

当浏览器引擎确定可以安全调用你的代码时执行的 function。微任务(microtask)的执行顺序在所有进行中的任务(pending task)完成之后,在对浏览器的事件循环产生控制(yielding control to the browser's event loop)之前。

返回值

无 undefined

  • 兼容性: 支持新式浏览器,但不包括IE11。
  • 用法: queueMicrotask用于将微任务排入微任务队列中,通常在Promise处理中使用,以确保微任务在宏任务之后执行。示例:
queueMicrotask(() => {
  // 这里执行微任务
});

MutationObserver:

MutationObserver 接口提供了监视对 DOM 树所做更改的能力。它被设计为旧的 Mutation Events 功能的替代品,该功能是 DOM3 Events 规范的一部分。

构造函数

MutationObserver()

创建并返回一个新的 MutationObserver 它会在指定的 DOM 发生变化时被调用。

方法

disconnect()

阻止 MutationObserver 实例继续接收的通知,直到再次调用其 observe() 方法,该观察者对象包含的回调函数都不会再被调用。

observe()

配置 MutationObserver 在 DOM 更改匹配给定选项时,通过其回调函数开始接收通知。

takeRecords()

从 MutationObserver 的通知队列中删除所有待处理的通知,并将它们返回到 MutationRecord 对象的新 Array 中。

  • 兼容性: 在大多数现代浏览器中支持,但Node.js不支持,而IE11也支持。
  • 用法: MutationObserver用于监视DOM树的变化,并在发生变化时触发回调函数。这通常用于监听DOM元素的变化。示例:
// 选择需要观察变动的节点
const targetNode = document.getElementById("some-id");

// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: true, subtree: true };

// 当观察到变动时执行的回调函数
const callback = function (mutationsList, observer) {
  // Use traditional 'for loops' for IE 11
  for (let mutation of mutationsList) {
    if (mutation.type === "childList") {
      console.log("A child node has been added or removed.");
    } else if (mutation.type === "attributes") {
      console.log("The " + mutation.attributeName + " attribute was modified.");
    }
  }
};

// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);

// 以上述配置开始观察目标节点
observer.observe(targetNode, config);

// 之后,可停止观察
observer.disconnect();

setImmediate:

setImmediate参数设置:第一个参数是需要执行的方法,第二个参数到第n个参数是传入方法中的参数。

setImmediate表示立即执行,它是宏任务,回调函数会被放置到事件循环的check阶段。
在应用中如果大量的计算型任务,它是不适合放在主线程中执行的,因为计算任务会阻塞主线程,主线程一旦被阻塞,其他任务就需要等待,
可以通过setImmediate方法将任务放入事件循环中的check阶段,因为代码在这一个阶段执行不会阻塞主线程,也不会阻塞事件循环
 

  • 兼容性: 主要支持IE10和IE11,以及一些较旧版本的Edge浏览器。
  • 用法: setImmediate用于将函数排入宏任务队列,以便在当前任务完成后立即执行。示例:
function sleep(delay) {
  var start = new Date().getTime()
  while (new Date().getTime() - start < delay) {
    continue
  }
  console.log('ok')
}

console.log('start')
setImmediate(sleep, 2000)
console.log('end')

先打印出start,然后再打印end,最后等待2000ms后打印ok。 

setTimeout:

  • 兼容性: 在Node.js和几乎所有现代浏览器中都支持。
  • 用法: setTimeout用于在一定时间后将函数排入宏任务队列。它是处理异步操作的常用方法。示例:
setTimeout(() => {
  // 这里执行宏任务
}, 1000);

异步任务---函数封装

  •  1.定义函数
  •  2.调用核心api(queueMicrotask,MutationObserver,setTimeout)
  •  3.使用封装的函数

 在then方法中使用无论promse是啥状态都需要直接或者间接的执行回调函数

/**
 * 异步任务--函数封装
 * 1.定义函数
 * 2.调用核心api(queueMicrotask,MutationObserver,setTimeout)
 * 3.使用封装的函数
 */

//1.定义函数
function runAsyncTask(callback) {
  //  2.调用核心api(queueMicrotask,MutationObserver,setTimeout)
  if (typeof queueMicrotask === "function") {
    queueMicrotask(callback);
  } else if (typeof MutationObserver == "function") {
    const obs = new MutationObserver(callback);
    const divNode = document.createElement("div");
    obs.observe(divNode, { childList: true });
    divNode.innerHTML = "贾公子";
  } else {
    setTimeout(callback, 0);
  }
}

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

class MyPromise {
  state = PENDING;
  result = undefined;
  #handlers = [];
  constructor(func) {
    const resolve = (result) => {
      if (this.state == PENDING) {
        this.state = FULFILLED;
        this.result = result;
        this.#handlers.forEach(({ onFulfilled }) => {
          onFulfilled(this.result);
        });
      }
    };

    const reject = (result) => {
      if (this.state == PENDING) {
        this.state = REJECTED;
        this.result = result;
        this.#handlers.forEach(({ onRejected }) => {
          onRejected(this.result);
        });
      }
    };

    func(resolve, reject);
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (x) => x;

    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (x) => {
            throw x;
          };
    // 3.使用封装的函数
    if (this.state === FULFILLED) {
      runAsyncTask(() => {
        onFulfilled(this.result);
      });
    } else if (this.state === REJECTED) {
      runAsyncTask(() => {
        onRejected(this.result);
      });
    } else if (this.state === PENDING) {
      this.#handlers.push({
        // 成功或者失败后调用 onFulfilled
        // onFulfilled 执行之后调用runAsyncTask(传入:promise传入的回调)
        //runAsyncTask内部执行了回调 onFulfilled
        onFulfilled: () => {
          runAsyncTask(() => {
            onFulfilled(this.result);
          });
        },
        onRejected: () => {
          runAsyncTask(() => {
            onRejected(this.result);
          });
        },
      });
    }
  }
}

// 测试代码
console.log("TOP");
const p = new MyPromise((resolve, reject) => {
  // resolve("success");
  reject("error");
});
p.then(
  (result) => {
    console.log("成功回调", result);
  },
  (err) => {
    console.log("失败回调", err);
  }
);
console.log("BOTTOM");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值