Promise 基本原理

本文深入探讨了Promise的工作原理及其实现细节,并解析了不同环境下Promise如何作为异步任务执行,包括MutationObserver和setTimeout的不同应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

author: 陈家宾
email: 617822642@qq.com
date: 2018/2/23

Promise 基本原理

var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise(fn) {
    var state = PENDING;
    var value = null;
    var handlers = [];

    doResolve(fn, resolve, reject)

    function fulfill(result) {}
    // state=fulfilled, value=result

    function reject(error) {}
    // state=rejected, value=error

    function resolve(result) {}
    // if then in result(result 是个 promise), 执行 doResolve
    // else 执行 fulfill

    function doResolve(fn, onFulfilled, onRejected) {}
    // 给 fn 传入 resolve 和 reject 函数
    // resolve 函数,执行 onFulfilled
    // reject 函数,执行 onRejected

    function handle(handler) {}
    // if PENDING, push handler
    // if FULFILLED, 执行 handler 中的 onFulfilled 函数
    // if REJECTED, 执行 handler 中的 onRejected 函数

    this.done = function (onFulfilled, onRejected) {}
    // 异步(setTimeout 0)执行 handler({onFulfilled, onRejected})

    this.then = function (onFulfilled, onRejected) {
        var self = this
        return new Promise((resolve,reject)=>{
            return self.done(result=>{
                if onFulfilled
                    return resolve(onFulfilled(result))
                else 
                    return resolve(result)
            }, err=>{
                if onRejected
                    return resolve(onRejected(err))
                else
                    return reject(err)
            })
        })
    }
}

基本语法

new Promsie((resolve, reject) => {
    // function1()
    resolve()
}).then(() => {
    // function2()
})

这里 function1 同步执行,function2 异步执行

Promise 本轮循环 & 次轮循环

本来以为 Promise 的内容就到此为止了,后来看到了阮老师的一篇博客,里面说异步分两种,what?!!

异步任务可以分成两种。

  • 追加在本轮循环的异步任务
  • 追加在次轮循环的异步任务

Node 规定,process.nextTickPromise的回调函数,追加在本轮循环,即同步任务一旦执行完成,就开始执行它们。而setTimeoutsetIntervalsetImmediate的回调函数,追加在次轮循环。

异步分两种已经让我大开眼界,但在我的知识世界里,Promise 不是用 setTimeout 来实现异步的吗,为什么 Promise 和 setTimeout 还分属于不同的异步类型里呢?

OK,马上找一下 Promise 的 polyfill 原码

if (isNode) {
  scheduleFlush = useNextTick();
} else if (BrowserMutationObserver) { // BrowserMutationObserver = window.MutationObserver
  scheduleFlush = useMutationObserver();
} else if (isWorker) { // typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined'
  scheduleFlush = useMessageChannel();
} else if (browserWindow === undefined && typeof require === 'function') { // browserWindow = typeof window !== 'undefined' ? window : undefined
  scheduleFlush = attemptVertx();
} else {
  scheduleFlush = useSetTimeout();
}

原来 Promise 的异步不仅仅只是 setTimeout,这里会根据不同环境来采用不同的实现方式,浏览器中主要用了 MutationObserver 和 setTimeout

我们先来看一下 MutationObserver 的兼容性(下图参考 https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver

由上图可知,使用 polyfill,从以上版本开始,Promise 是由 MutationObserver 实现的本轮循环的异步任务,低于以上版本的,则是由 setTimeout 实现的次轮循环的异步任务(本轮循环在次轮循环之前执行)。其带来的具体差别如下:

// ie11
setTimeout(function () {console.log(1)});
Promise.resolve().then(function () {
  console.log(2);
});
// 输出结果为 2 1

// ie10
setTimeout(function () {console.log(1)});
Promise.resolve().then(function () {
  console.log(2);
});
// 输出结果为 1 2

其他

shim VS polyfill

  • shim,是一种能给旧环境带来新 API 的库
  • polyfill 则是针对浏览器的一种 shim,一种补充

单词 polyfill 的由来:

Polyfilla is a UK product known as Spackling Paste in the US. With that in mind: think of the browsers as a wall with cracks in it. These [polyfills] help smooth out the cracks and give us a nice smooth wall of browsers to work with.——Remy Sharp


参考资料

  1. 《Node 定时器详解》,阮一峰,2018年2月23日,http://www.ruanyifeng.com/blog/2018/02/node-event-loop.html
  2. What is the difference between a shim and a polyfill? ,stack overflow,closed at Jul 30 ‘15,https://stackoverflow.com/questions/6599815/what-is-the-difference-between-a-shim-and-a-polyfill
  3. 《Speaking JavaScript》,Axel Rauschmayer,March 2014,http://speakingjs.com/es5/ch30.html#id1267739
### Promise 实现原理与 JavaScript 源码解析 #### 一、Promise 的基本概念 Promise 是一种异步编程解决方案,它遵循 Promises/A+ 规范[^1]。其核心在于封装了一个可能具有延迟的结果的操作,并提供了统一的接口来处理这些操作的成功或失败。 #### 二、Promise 的状态管理 Promise 对象有三种主要的状态: - **Pending**: 初始状态,既未完成也未失败。 - **Fulfilled**: 成功状态,表示操作成功完成。 - **Rejected**: 失败状态,表示操作未能完成并抛出了错误。 一旦状态从 Pending 变更为 Fulfilled 或 Rejected,则该状态不可逆[^4]。 #### 三、Promise 的核心方法实现 以下是 Promise 的几个重要方法及其工作原理: ##### 1. `then` 方法 `then` 方法用于注册当 Promise 被解决时要执行的回调函数。它的返回值也是一个新的 Promise 实例,从而支持链式调用[^2]。 ```javascript // 示例代码展示 then 方法的工作机制 new Promise((resolve) => { resolve('你好'); }).then((res) => { console.log(res); // 输出 '你好' }); ``` ##### 2. `catch` 方法 `catch` 方法专门用来捕获 Promise 链中的异常。其实质是对 `then` 方法的一个简化版本,仅接收拒绝回调作为参数[^3]。 ```javascript // catch 方法的核心逻辑 Promise.prototype.catch = function(onRejected) { return this.then(null, onRejected); }; ``` ##### 3. `finally` 方法 无论 Promise 是否被解决或拒绝,`finally` 方法都会被执行。它是基于 `then` 方法的一种扩展,主要用于清理资源或其他无关业务逻辑的任务。 ```javascript // finally 方法模拟实现 Promise.prototype.finally = function(callback) { return this.then( (value) => Promise.resolve(callback()).then(() => value), (reason) => Promise.resolve(callback()).then(() => { throw reason }) ); }; ``` #### 四、静态方法的功能剖析 除了实例方法外,Promise 还提供了一些常用的静态方法: ##### 1. `Promise.resolve(value)` 如果传入的是一个已经存在的 Promise,则直接返回这个 Promise;如果是其他类型的值,则将其转换为已解决的 Promise。 ```javascript const resolvedValue = Promise.resolve(42).then((val) => val); // 返回值为 42 ``` ##### 2. `Promise.reject(reason)` 创建一个带有指定原因的拒绝态 Promise。 ```javascript const rejectedReason = Promise.reject(new Error('Error occurred')); rejectedReason.catch(err => console.error(err.message)); // 打印 'Error occurred' ``` ##### 3. `Promise.all(iterable)` 接受一个可迭代对象(如数组),只有所有的 Promise 都解决了才会继续往下走,否则立即进入 Reject 状态。 ```javascript Promise.all([Promise.resolve(1), Promise.resolve(2)]).then(results => console.log(results)); // 结果为 [1, 2] ``` #### 五、完整的简易版 Promise 实现 下面给出一个简单的 Promise 类实现,涵盖了上述提到的主要功能: ```javascript class MyPromise { constructor(executor) { this.state = 'pending'; this.value = undefined; this.reason = undefined; const resolve = (value) => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; } }; const reject = (reason) => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; } }; try { executor(resolve, reject); } catch (error) { reject(error); } } then(onFulfilled, onRejected) { return new MyPromise((resolve, reject) => { if (this.state === 'fulfilled') { setTimeout(() => { try { resolve(onFulfilled(this.value)); } catch (err) { reject(err); } }, 0); } if (this.state === 'rejected') { setTimeout(() => { try { reject(onRejected(this.reason)); } catch (err) { reject(err); } }, 0); } }); } catch(onRejected) { return this.then(null, onRejected); } } ``` 此代码片段展示了如何手动构建一个符合 Promises/A+ 标准的基础 Promise 架构[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值