Promise 原理解析 (渐进实例详解)

本文通过逐步实现Promise,深入探讨Promise的工作原理及其核心特性。从简单的封装到实现完整的Promise/A+规范,包括状态管理、链式调用、错误处理等功能。

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

前言

Promise 前端开发或多或少都有了解或使用到。 抽空梳理了一下,阅读本章希望能帮助了解Promise到底是怎么实现的。 我们采用渐进法,通过Demo 逐步实现Promise来体会Promise的用法。
Promise 规范有很多, 如 Promise/A, Promise/B, Promise/D == 以及 Promise/A 的升级 Promise/A+ , 有兴趣可以去了解下。 最终ES6中采用了 Promise/A+ 规范。 所以本篇文章是按照Promise/A+== 规范来编写的。(不想看英文版的转至 Promise/A+ 规范
中文翻译

Promise概要图
在这里插入图片描述

先来抛个砖

我们从一个场景开始讲解。 考虑下面demo 获取用户账号的请求处理:

//不使用Promise        
ajax.get('some_url', function (result) {
    //do something
    console.log(result.accountNumber );
});

//使用Promise
new Promise(function (resolve) {
    //异步请求
    ajax.get('some_url', function (results) {
        resolve(results.accountNumber )
    })
}).then(function (accountNumber ) {
    //do something
    console.log(accountNumber );
})

看起来好像不适用Promise 更简洁下。 非也! 设想一下, 如果好几个依赖前置请求都是异步, 此时如果没有Promise , 那回调函数要一层一层嵌套, 看起来就很不舒服了。
如下:

//不使用Promise        
ajax.get('some_url', function (accountNumber ) {
    //do something
    ajax.get('getId', id, function (name) {
        //do something
        ajax.get('getName', name, function (course) {
            //dong something
            ajax.get('getCourse', function (courseDetail) {
                //do something
            })
        })
    })
});

//使用Promise
function getAccountNumber (url) {
    return new Promise(function (resolve) {
        //异步请求
        ajax.get(url, function (accountNumber ) {
            resolve(accountNumber )
        })
    })
}
getUserId('some_url').then(function (accountNumber ) {
    //do something
    return getId(accountNumber );
}).then(function (name) {
    //do something
    return getName(name);
}).then(function (course) {
    //do something
    return getCourse(course);
}).then(function (courseDetail) {
    //do something
});

来引个玉 (实现原理渐进)

说到底, Promise 也还是使用回调函数, 只不过是把回调封装在了内部, 我们使用上一直通过then方法的链式调用, 书写和理解上会更加直观和简洁。

1. 简单的实现

//简单的实现
class Promise {
    callbacks = [];
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {
        this.callbacks.push(onFulfilled);
    }
    _resolve(value) {
        this.callbacks.forEach(fn => fn(value));
    }
}

//Promise应用
let p = new Promise(resolve => {
    setTimeout(() => {
        console.log('done');
        resolve('6秒');
    }, 6000);
}).then((tip) => {
    console.log(tip);
})

这个简单版本大致逻辑是:

实例化Promise 时,其类构造函数初始化执行了回调函数,并将绑定了当前实例的==_resolve方法作为参数回传给回到函数。接着调动Promise对象的then方法, 注册异步操作完成后的回调函数。 当异步操作完成时,调用resolve方法, 该方法执行then==方法注册的回调函数。

这里then 方法注册完成时的回到是一个数组, so then方法可以多次调用。注册的函数会在异步操作完成后根据添加的顺序依次执行。
如下:

//then 的说明
let p = new Promise(resolve => {
    setTimeout(() => {
        console.log('done');
        resolve('6秒');
    }, 6000);
});

p.then(tip => {
    console.log('then1', tip);
});

p.then(tip => {
    console.log('then2', tip);
});

仔细的同学可以看出, then方法应该是链式调用。 上面的调用方式就显得有点啰嗦了。 我们来修改下我们的简单实现源码, 即在then 中 return this 就OK了。
实例:

//极简的实现+链式调用
class Promise {
    callbacks = [];
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {
        this.callbacks.push(onFulfilled);
        return this;//看这里
    }
    _resolve(value) {
        this.callbacks.forEach(fn => fn(value));
    }
}

let p = new Promise(resolve => {
    setTimeout(() => {
        console.log('done');
        resolve('6秒');
    }, 6000);
}).then(tip => {
    console.log('then1', tip);
}).then(tip => {
    console.log('then2', tip);
});

2. 加入延时机制

问题来了, 上面的Promise 实现如果在then方法注册回调函数之间,resolve就执行了, 肿么办? 这样回调函数就不会执行到了。 比如我们把上栗中setTimeout去掉:

//同步执行了resolve
let p = new Promise(resolve => {
    console.log('同步执行');
    resolve('同步执行');
}).then(tip => {
    console.log('then1', tip);
}).then(tip => {
    console.log('then2', tip);
});

执行结果只打印显示了 “同步执行”。 再回去看下Promise的源码, 发现resolve执行时, callbacks 还是空数组, 回调函数还没来得急注册上去。

这显然是不允许的。 Promises/A++规范明确要求回调需要通过异步方式执行, 用以保证一致可靠的执行顺序。 那我们加入一些处理,保证在resolve执行之前,then方法已经注册完所有回调。 在来改造下我们resolve函数:

//极简的实现+链式调用+延迟机制
class Promise {
    callbacks = [];
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {
        this.callbacks.push(onFulfilled);
        return this;
    }
    _resolve(value) {
        setTimeout(() => {//看这里
            this.callbacks.forEach(fn => fn(value));
        });
    }
}

resove 中增加定时器, 通过定时器,将resolve 中执行的回调函数放置到JS任务队列末尾, 以保证在resolve执行时, then方法的回调函数已经注册完成。

当然这样依然存在一个问题, 在resolve 执行后, 在通过 then 注册上来的回调好像都没有机会执行了!!!! 如下示例: 加上延迟后 then1 , then2 可以打印出来, 但是 then3 依然不OK。

let p = new Promise(resolve => {
    console.log('同步执行');
    resolve('同步执行');
}).then(tip => {
    console.log('then1', tip);
}).then(tip => {
    console.log('then2', tip);
});

setTimeout(() => {
    p.then(tip => {
        console.log('then3', tip);
    })
});

所以接下来我们需要增加一个状态,并且保存 resolve 的值。 go on~

3. 加入状态

为了解决上面提到的问题, 我们需要加入状态机制, 也就是大家熟知的pending, fulfilled, rejected

Promises/A+ 规范中明确规定了, pending 可以转化为fulfilledrejected 并且只能转化一次。 也就是说如果pending 转为 fulfiled 就不能再转化到rejected。 并且 fulfilled 和 rejected 状态只能pending转化而来, 两者之间不能互相转换。

在这里插入图片描述
增加状态后的代码为:

//简单的实现+链式调用+延迟机制+状态
class Promise {
    callbacks = [];
    state = 'pending';//增加状态
    value = null;//保存结果
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {
        if (this.state === 'pending') {//在resolve之前,跟之前逻辑一样,添加到callbacks中
            this.callbacks.push(onFulfilled);
        } else {//在resolve之后,直接执行回调,返回结果了
            onFulfilled(this.value);
        }
        return this;
    }
    _resolve(value) {
        this.state = 'fulfilled';//改变状态
        this.value = value;//保存结果
        this.callbacks.forEach(fn => fn(value));
    }
}

resolve 执行时, 会将状态设置为 fulfilled , 并把 value 的值存起来, 在此之后调用 then 添加的新回调都会立即执行, 直接返回保存的value值。

注: 当增加完状态之后, 原先的_resolve中的定时器可以去掉了。 当resolve 同步执行, 虽然callbacks 为空, 回调函数还没来得及注册, 但没关系, 因为后面注册上来时, 判断状态为 fulfilled后, 会立即遍历执行回调。

4. 真正的链式调用

有个非非非常重要的问题, 优秀的你应该发现了, 上面提到的链式调用,只是在then方法中 **return 了 this **, 使得 Promise 实例可以多次调用 then方法,但因为是同一个实例, 调用再多次then也只能返回相同的一个结果。 SO , 这里满足不了我们真正的使用需求。

真正的链式Promise 是指在当前Promise 达到 fulfilled 状态后, 即开始进行下一个Promise (后邻Promise)。 那么我们如何衔接当前Promise 和后邻Promise呢? 这里是重点!!!

先来看下完整的实现源码:

//完整的实现
class Promise {
    callbacks = [];
    state = 'pending';//增加状态
    value = null;//保存结果
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {
        return new Promise(resolve => {
            this._handle({
                onFulfilled: onFulfilled || null,
                resolve: resolve
            });
        });
    }
    _handle(callback) {
        if (this.state === 'pending') {
            this.callbacks.push(callback);
            return;
        }
        //如果then中没有传递任何东西
        if (!callback.onFulfilled) {
            callback.resolve(this.value);
            return;
        }
        var ret = callback.onFulfilled(this.value);
        callback.resolve(ret);
    }
    _resolve(value) {

        if (value && (typeof value === 'object' || typeof value === 'function')) {
            var then = value.then;
            if (typeof then === 'function') {
                then.call(value, this._resolve.bind(this));
                return;
            }
        }

        this.state = 'fulfilled';//改变状态
        this.value = value;//保存结果
        this.callbacks.forEach(callback => this._handle(callback));
    }
}

先来 简单说说要点,然后在逐一通过例子去详解。

  • then 方法中, 创建返回了新的Promise 实例, 这是串行Promise的基础, 是实现真正链式调用的根本。
  • handle 方法是Promise 内部的方法。 then 方法传入的形参onFulfilled以及新创建Promise 实例时传入的resolve匀被 push 到当前 Promise 的callbacks
    队列中, 这是衔接当前 promise 和 后邻 promise 的关键。
  • resolve 方法中会先检查value 是不是 Promise 对象, 如果是一个新的 Promise , 那么先不改变当前 promise 的状态。

来一个栗子(这里直贴主要代码片段,能理解即可):

/**
 * 模拟异步请求
 * @param {*} url  请求的URL
 * @param {*} s  指定该请求的耗时,即多久之后请求会返回。单位秒
 * @param {*} callback 请求返回后的回调函数
 */
const mockAjax = (url, s, callback) => {
    setTimeout(() => {
        callback(url + '异步请求耗时' + s + '秒');
    }, 1000 * s)
}


//Demo1
new Promise(resolve => {
    mockAjax('getId', 1, function (result) {
        resolve(result);
    })
}, 'first').then(result => {
    console.log(result);
});

为了清楚的看到当前是哪个实例, 在Promise 的实现中, 增加了一个 name 属性, 标识当前 Promise 实例, 上栗中传了‘first’表示第一个Promise实例。
下面为输入日志:

Promise[first]:constructor
Promise[first]:then
Promise[then1]:constructor
Promise[first]:_handle state= pending
Promise[first]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
=> Promise { callbacks: [], name: 'then1', state: 'pending', value: null }
Promise[first]:_resolve
Promise[first]:_resolve value= getUserId异步请求耗时1秒
Promise[first]:_handle state= fulfilled
getUserId异步请求耗时1秒
Promise[then1]:_resolve
Promise[then1]:_resolve value= undefined

通过上面打印日志可以看到:

  1. 构造first实例,立即执行mackAjax(‘getUserId’,callback);
  2. 调用first的then方法,注册first完成时的回调函数。
  3. then函数内部构造了一个新的Promise实例:then1。立即执行first的_handle方法
  4. 此时first还是pending的状态
  5. first._handle中就把注册在first成功后的回调和then1的状态改变操作接口保存在first内 部
  6. 至此当前线程执行结束。返回的是then1的Promise实例。
  7. 1s后,异步请求返回,要改变first的状态和结果,执行resolve(result)
  8. first的值被改变,内容为异步请求返回的结果:“getUserId异步请求耗时1s”
  9. first的状态变成fulfilled
  10. first注册的成功回调被执行,打印出了"getUserId异步请求耗时1秒"
  11. 然后再调用then1._resolve
  12. 改变then1的值和状态,因为first注册的回调函数没有返回值,所以then1的值为undefined

这里补一张直观的流程图

在这里插入图片描述
如果我们吧上面异步的请求改成同步会是什么结果呢?

new Promise(resolve => {
    resolve('getUserId同步请求');
}, 'first').then(result => {
    console.log(result);
});

//打印日志
Promise[first]:constructor
Promise[first]:_resolve
Promise[first]:_resolve value= getUserId同步请求
Promise[first]:then
Promise[then1]:constructor
Promise[first]:_handle state= fulfilled
getUserId同步请求
Promise[then1]:_resolve
Promise[then1]:_resolve value= undefined
=> Promise {
  callbacks: [],
  name: 'then1',
  state: 'fulfilled',
  value: undefined }

感兴趣的可以自己去分析一下。

至此, 应该基本能够理解真正的链式调用了吧。 如果是多个then 时,要知道它是可以无限写下去的, 上一级 onFulfilled return 的值, 会变成下一级onFulfilled的结果

那么接下来我们对比下下面的两个Demo:

Demo2

//Demo2
new Promise(resolve => {
    mockAjax('getUserId', 1, function (result) {
        resolve(result);
    })
}, 'first').then(result => {
    console.log(result);
    //对result进行第一层加工
    let exResult = '前缀:' + result;
    return exResult;
}).then(exResult => {
    console.log(exResult);
    let xxResult = exResult + ':后缀';
    return xxResult;
}).then(finalResult => {
    console.log(finalResult);
});

Demo3

//Demo3 === Demo2
new Promise(resolve => {
    mockAjax('getUserId', 1, function (result) {
        resolve(result);
    })
}, 'first').then(result => {
    console.log(result);
    //对result进行第一层加工
    let exResult = '前缀:' + result;
    console.log(exResult);

    let finalResult = exResult + ':后缀';
    console.log(finalResult);
});

感觉demo3 的写法更好些!!! 那链式调用的意义在哪里?

仔细的同学一定发现了, 目前为止Promise 源码中还有一块代码没有被执行到。 也就是下面 _resolve 中最开始的 if 代码块, 好像从来没有被执行到。

 _resolve(value) {

        if (value && (typeof value === 'object' || typeof value === 'function')) {
            var then = value.then;
            if (typeof then === 'function') {
                then.call(value, this._resolve.bind(this));
                return;
            }
        }

        this.state = 'fulfilled';//改变状态
        this.value = value;//保存结果
        this.callbacks.forEach(callback => this._handle(callback));
    }

从代码上看, 它是对resolve 中的值做了一个特殊的判断, 判断resolve的值是否为Promise 实例, 如果是Promise 实例, 那么就把当前Promise实例的改变状态的接口重新注册到resolve的值Promise 的成功回调中, 也就是说当前 Promise 实例的状态要依赖resolve的值的Promise实例的状态。

//Demo4
const getId = function () {
    new Promise(resolve => {
        mockAjax('getId', 1, function (result) {
            resolve(result);
        })
    }, 'getUserId');
}
const getId = function () {
    new Promise(resolve => {
        mockAjax('getId', 2, function (result) {
            resolve(result);
        })
    }, 'getId');
}
const getName = function () {
    new Promise(resolve => {
        mockAjax('getName', 3, function (result) {
            resolve(result);
        })
    }, 'getName');
}
const getCourse = function () {
    new Promise(resolve => {
        mockAjax('getCourse', 4, function (result) {
            resolve(result);
        })
    }, 'getCourse');
}
getId()
    .then(id => {
        console.log(id);
        return geId();
    }).then(name => {
        console.log(name);
        return getName()
    }).then(course => {
        console.log(course);
        return getCourse()
    }).then(courseDetail => {
        console.log(courseDetail);
    });

运行结果如下:

Promise[getId]:constructor
Promise[getId]:then
Promise[then1]:constructor
Promise[getId]:_handle state= pending
Promise[getId]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
Promise[then1]:then
Promise[then2]:constructor
Promise[then1]:_handle state= pending
Promise[then1]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
Promise[then2]:then
Promise[then3]:constructor
Promise[then2]:_handle state= pending
Promise[then2]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
Promise[then3]:then
Promise[then4]:constructor
Promise[then3]:_handle state= pending
Promise[then3]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
=> Promise { callbacks: [], name: 'then4', state: 'pending', value: null }
Promise[getId]:_resolve
Promise[getId]:_resolve value= getId异步请求耗时1秒
Promise[getId]:_handle state= fulfilled
getId异步请求耗时1秒
Promise[getId]:constructor
Promise[then1]:_resolve
Promise[then1]:_resolve value= Promise {
  callbacks: [],
  name: 'getId',
  state: 'pending',
  value: null }
Promise[getId]:then
Promise[then5]:constructor
Promise[getId]:_handle state= pending
Promise[getId]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
Promise[getId]:_resolve
Promise[getId]:_resolve value= getId异步请求耗时2秒
Promise[getId]:_handle state= fulfilled
Promise[then1]:_resolve
Promise[then1]:_resolve value= getId异步请求耗时2秒
Promise[then1]:_handle state= fulfilled
getId异步请求耗时2秒
Promise[getName]:constructor
Promise[then2]:_resolve
Promise[then2]:_resolve value= Promise {
  callbacks: [],
  name: 'getName',
  state: 'pending',
  value: null }
Promise[getName]:then
Promise[then6]:constructor
Promise[getName]:_handle state= pending
Promise[getName]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
Promise[then5]:_resolve
Promise[then5]:_resolve value= undefined
Promise[getName]:_resolve
Promise[getName]:_resolve value= getName异步请求耗时3秒
Promise[getName]:_handle state= fulfilled
Promise[then2]:_resolve
Promise[then2]:_resolve value= getName异步请求耗时3秒
Promise[then2]:_handle state= fulfilled
getName异步请求耗时3秒
Promise[getCourse]:constructor
Promise[then3]:_resolve
Promise[then3]:_resolve value= Promise {
  callbacks: [],
  name: 'getCourse',
  state: 'pending',
  value: null }
Promise[getCourse]:then
Promise[then7]:constructor
Promise[getCourse]:_handle state= pending
Promise[getCourse]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
Promise[then6]:_resolve
Promise[then6]:_resolve value= undefined
Promise[getCourse]:_resolve
Promise[getCourse]:_resolve value= getCourse异步请求耗时4秒
Promise[getCourse]:_handle state= fulfilled
Promise[then3]:_resolve
Promise[then3]:_resolve value= getCourse异步请求耗时4秒
Promise[then3]:_handle state= fulfilled
getCourse异步请求耗时4秒
Promise[then4]:_resolve
Promise[then4]:_resolve value= undefined
Promise[then7]:_resolve
Promise[then7]:_resolve value= undefined

错误处理

在异常操作失败时,标记其状态为rejected , 并执行注册的失败回调。

//demo reject
new Promise((resolve, reject) => {

    mockAjax('getId', 1, function (result, error) {
        if (error) {
            reject(error)
        } else {
            resolve(result);
        }
    })

}, 'getId').then(result => {
    console.log(result);
}, error => {
    console.log(error);
});

有了之前处理fulfilled 状态的经验, 支持错误处理就变的很容易了, 只需要在注册回调, 处理状态变更上都加入新的 reject 逻辑:

//完整的实现+reject
class Promise {
    callbacks = [];
    state = 'pending';//增加状态
    value = null;//保存结果
    constructor(fn) {
        fn(this._resolve.bind(this), this._reject.bind(this));
    }
    then(onFulfilled, onRejected) {
        return new Promise((resolve, reject) => {
            this._handle({
                onFulfilled: onFulfilled || null,
                onRejected: onRejected || null,
                resolve: resolve,
                reject: reject
            });
        });
    }
    _handle(callback) {
        if (this.state === 'pending') {
            this.callbacks.push(callback);
            return;
        }

        let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;

        if (!cb) {//如果then中没有传递任何东西
            cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
            cb(this.value);
            return;
        }

        let ret = cb(this.value);
        cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
        cb(ret);
    }
    _resolve(value) {

        if (value && (typeof value === 'object' || typeof value === 'function')) {
            var then = value.then;
            if (typeof then === 'function') {
                then.call(value, this._resolve.bind(this), this._reject.bind(this));
                return;
            }
        }

        this.state = 'fulfilled';//改变状态
        this.value = value;//保存结果
        this.callbacks.forEach(callback => this._handle(callback));
    }
    _reject(error) {
        this.state = 'rejected';
        this.value = error;
        this.callbacks.forEach(callback => this._handle(callback));
    }
}

异常处理

上面介绍了错误处理, 是指在Promise 的构造函数中发现的错误, 并通过reject 通知的。 如果在执行成功或者失败回调时出现了异常,该如何处理??
对于类似异常, 处理也很简单, 可以使用try-catch 捕获错误, 然后将相应的promise 状态设置为rejected 状态。 我们来改造下==_handle==方法如下:

_handle(callback) {
        if (this.state === 'pending') {
            this.callbacks.push(callback);
            return;
        }

        let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;

        if (!cb) {//如果then中没有传递任何东西
            cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
            cb(this.value);
            return;
        }

        let ret;

        try {
            ret = cb(this.value);
            cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
        } catch (error) {
            ret = error;
            cb = callback.reject
        } finally {
            cb(ret);
        }

    }

无论是错误还是异常, 最终都是通过reject 实现的, 可见最终对于错误以及异常的处理, 都可以通过then中的错误回调来处理。 所以可以单独增加一个catch 方法, 它是 .then(null, rejection) 的别名。
如下:

 then(onFulfilled, onRejected) {
        return new Promise((resolve, reject) => {
            this._handle({
                onFulfilled: onFulfilled || null,
                onRejected: onRejected || null,
                resolve: resolve,
                reject: reject
            });
        });
    }
    catch(onError){
      this.then(null, onError);
    }

Finally 方法

这个方法不用多说,想必大家都很熟。 不过Promise最后的状态如何, 都要执行的操作。 finally 的实现其实很简单:

catch(onError) {
    console.log('Promise[%s]:catch', this.name);
    this.then(null, onError);
  }
  finally(onDone){
    console.log('Promise[%s]:finally', this.name);
    this.then(onDone, onDone);
  }

静态方法

除了上面提到的Promise 实例的方法外, Promise 还提供了 Promise.resolve 和 Promise.reject 方法。 用于将非Promise实例包装为Promise实例。

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))

这里 静态方法有个特点: 如果其参数为promise 则什么都不做,返回参数; 如果参数为一个非promise 对象, 则创建并返回一个promise

再来看看这个例子:

Promise.resolve(getNameById(id)).then(name => {
  console.log(name);
});

在实现Promise.resolve 之前, 先看下它的参数部分有哪些情况:

1.参数是一个Promise实例
如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。

2.参数是一个thenable对象
thenable对象指的是具有then方法的对象,比如下面这个对象。

let thenable = {
  then: function(onFulfilled) {
    onFulfilled(42);
  }
};

Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。

let thenable = {
  then: function(onFulfilled) {
    onFulfilled(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});

上面代码中,thenable对象的then方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then方法指定的回调函数,输出 42。

3. 参数不是具有then方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。

4. 不带任务参数
Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。

 finally(onDone) {
    this.then(onDone, onDone);
  }
  static resolve(value) {
    if (value && value instanceof Promise) {
      return value;
    } else if (value && typeof value === 'object' && typeof value.then === 'function') {
      let then = value.then;
      return new Promise(resolve => {
        then(resolve);
      });

    } else if (value) {
      return new Promise(resolve => resolve(value));
    } else {
      return new Promise(resolve => resolve());
    }
  }

Promise.reject与Promise.resolve类似,区别在于Promise.reject始终返回一个状态的rejected的Promise实例,而Promise.resolve的参数如果是一个Promise实例的话,返回的是参数对应的Promise实例,所以状态不一 定。

Promise.resolve / Promise.reject/ Promise.catch 内部实现

// 使用
Promise.resolve('is resolve').then(data=>{
   console.log(data) // is resolve 
})

// 内部实现
Promise.resolve = function(value) {
   return new Promise((resolve, reject) => { // 内部new一个Promise对象
       resolve(value)
   })
}

// reject 内部实现
Promise.reject = function(resason) {
    return new Promise((resolve, reject) => {
        reject(reason)
    })
}

// catch 内部实现
Promise.reject('is  reject').catch(e => {
   console.log(e)  // is reject
})

// 上面这段代码相当于下面这段代码
Promise.reject('is reject').then(null, e =>{ // then里直接走了失败的回调
    console.log(e)  // is reject
})

// 内部实现
Promise.prototype.catch = function(onReected) {
    return this.then(null, onRejected) // 相当与then里的成功回调只传个null
}

** Promise.all 内部实现**

// Promise.all 这个方法非常重要, 同时执行多个异步,并返回一个新的promise, 成功的值是一个数组, 该数组成员的顺序是传给Promise.all的顺序
// Promise.all 的使用
function usePromise(url, params){
   return new Promise((resolve, reject) => {
       setTimeout(function(err, data){
           if(err) reject(err)
           resolve(data)
        }, 2000)
   })
}

Promise.all(usePromise('url1',{}), usePromise(url2, {})).then(data => {
    console.log(data) // 全部读取成功后返回[data1,  data2, ....]
                                 // 需要注意的是,其中有一个失败,则走失败的回调函数
})

// 内部实现
Promise.all = function(promises) {  // promises 是个数组
    let arr = [];
    let i = 0;
    function processData(index, data) {
       arr[index] = data
    
       if(++i == promises.length){
          resolve(arr)
       }
    }

     for(let i = 0; i<promise.length; i++) {
        promsies[i].then(data => {
          // 因为Promise.all 最终返回的是一个数组成员按照顺序排序的数组,
          // 而异步执行,返回并不一定按照顺序, 所以需要传当前的i
           processData(i, data);  
        }, reject) // 如果其中一个失败,则调用reject
     }
}

Promise.race 内部实现

// 原生Promise.race的使用
// 一个成功就走成功的回调,一个失败就走失败的回调
Promise.race(promise1, promise2,...).then(data => {
    console.log(data);  // 可能返回promise1的结果, 也可能返回promise2的结果,看哪个返回的快就用哪个作为结果
})

// 内部实现
Promise.race = function(promises) { // promises 是数组
    return new Promise((resolve, reject) => {
        for(let i=0; i<promise.length; i++) {
           promises[i].then(resolve, reject)  //
        }
    })
}

总结

刚开始看promise源码的时候总不能很好的理解then和resolve函数的运行机理,但是如果反过来根据执行promise时的逻辑来推演,就不难理解了。这里一定要注意的点是:promise里面的then函数仅仅是注册了后续需要执行的代码,真正的执行是在resolve方法里面执行的,理清了这层,再来分析源码会省力的多。
现在回顾下Promise的实现过程,其主要使用了设计模式中的观察者模式
通过Promise.prototype.then和Promise.prototype.catch方法将观察者方法注册到被观察者Promise对象中,同时返回一个新的Promise对象,以便可以链式调用。
被观察者管理内部pending、fulfilled和rejected的状态转变,同时通过构造函数中传递的resolve和reject方法以主动触发状态转变和通知观察者。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值