0

0

promise对象的深入解析(附示例)

不言

不言

发布时间:2018-11-23 14:49:00

|

2413人浏览过

|

来源于segmentfault

转载

本篇文章给大家带来的内容是关于promise对象的深入解析(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

js中的异步,刚开始的时候都是用回调函数实现的,所以如果异步嵌套的话,就有出现回调地狱,使得代码难以阅读和难以维护,后来es6出现了promise,解决了回调地狱的问题。现在我们就自己写代码实现一下promise,这样才能深入理解promise的运行机制,对以后使用promise也能够更加得心应手。开始之前可以先看下promise的官网promise/A+

先来看下promise的用法

new Promise((resolve,reject)=>{
  resolve(1);
  reject(11);
}).then(res=>{
  console.log(res);
  setTimeout(()=>{
    return new Promise((resolve,reject)=>{
        resolve(2)
    }) 
  },1000)
}).then(res2=>{
  console.log(res2);
});

控制台打印
1
...1s later
2

先分析下上面这段代码,先提出几个问题
1.第一段resolve和reject都有,但是只输出了1,为什么?
2.then里的res是如何取到resolve中的值的?
3.promise是如何做到链式调用的?

状态机

promise中有个状态机的概念,先说下为什么要有状态机的概念呢,因为promise的状态是单向变化的,有三种状态,pending,fullfilled,rejected,而这三种状态只能从pending->fullfilled或者pending->rejected这两种形式,也就是说执行了fullfilled之后,就不会执行rejected。这就解释了上面的第一个问题。

下面我们来看下具体实现的完整代码

const PENDING = 'PENDING';
const FULLFILLED = 'FULLFILLED';
const REJECTED = 'REJECTED';    
class Promise{
    constructor(fn){
       this.status = PENDING;//状态
        this.data = undefined;//返回值
        this.defercb = [];//回调函数数组
        //执行promise的参数函数,并把resolve和reject的this绑定到promise的this
        fn(this.resolve.bind(this),this.reject.bind(this));
    }
    resolve(value){
        if(this.status === PENDING){
            //只能pending=>fullfied
            this.status = FULLFILLED;
            this.data = value;
            this.defercb.map(item=>item.onFullFilled());
        }
    }
    reject(value){
        if(this.status === PENDING){
            //只能pending=>rejected
            this.status = REJECTED;
            this.data = value;
            this.defercb.map(item=>item.onRejected());
        }
    }
    then(resolveThen,rejectThen){
       //如果没有resolveThen方法,保证值可以穿透到下一个then里有resolveThen的方法中
        resolveThen = typeof resolveThen === 'function' ? resolveThen : function(v) {return v};
        rejectThen = typeof rejectThen === 'function' ? rejectThen : function(r) {return r};
        //返回的都是promise对象,这样就可以保证链式调用了
        switch(this.status){
            case PENDING:
            return new Promise((resolve,reject)=>{
                const onFullFilled = () => {
                    const result = resolveThen(this.data);//这里调用外部then的resolveThen方法,将值传回去
                    //如果返回值是promise对象,执行then方法,取它的结果作为新的promise实例的结果,因为this.data会重新赋值
                    result instanceof Promise && result.then(resolve,reject);
                }
                const onRejected = ()=>{
                    const result = rejectThen(this.data);
                    result instanceof Promise && result.then(resolve,reject);
                }
                this.defercb.push({onFullFilled,onRejected});
            });
            break;
            case FULLFILLED:
               return new Promise((resolve,reject)=>{
                     const result = resolveThen(this.data);
                     result instanceof Promise && result.then(resolve,reject);
                     resolve(result);
               })
            break;
            case REJECTED:
               return new Promise((resolve,reject)=>{
                   const result = rejectThen(this.data);
                   result instanceof Promise && result.then(resolve,reject);
                   reject(result)
               })   
            break;
        }
    }
}

运行下面的例子

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1);
    }, 1000);
}).then((res2) => {
    console.log(res2);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
        }, 1000);
    });
}).then((res3) => {
    console.log(res3);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
        }, 1000);
    });
}).then((res4) => {
    console.log(res4);
});

控制台打印
...1s later
1
...1s later
2
...1s later
3
说明上面的实现是没有问题的
不过还有一个问题,就是事件循环的顺序问题,比如执行下面的代码

new Promise((resolve) => {
    resolve();
})
.then(() => {
    console.log('1');
})
.then(() => {
    console.log('2');
});
console.log('3');

并没有像预想中输出3,1,2,而是输出了1,2,3,原因就是因为我们的这个Promise是在主线程中,没有在下一个任务队列中,可以加上settimeout解决这个问题,不过这也只是为了让我们更好理解执行顺序而已,然而实际上是promise是属于微任务中的,而settimeout是属于宏任务,还是不太一样的

相关专题

更多
es6新特性
es6新特性

es6新特性有:1、块级作用域变量;2、箭头函数;3、模板字符串;4、解构赋值;5、默认参数;6、 扩展运算符;7、 类和继承;8、Promise。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

97

2023.07.17

es6新特性有哪些
es6新特性有哪些

es6的新特性有:1、块级作用域;2、箭头函数;3、解构赋值;4、默认参数;5、扩展运算符;6、模板字符串;7、类和模块;8、迭代器和生成器;9、Promise对象;10、模块化导入和导出等等。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

188

2023.08.04

JavaScript ES6新特性
JavaScript ES6新特性

ES6是JavaScript的根本性升级,引入let/const实现块级作用域、箭头函数解决this绑定问题、解构赋值与模板字符串简化数据处理、对象简写与模块化提升代码可读性与组织性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

220

2025.12.24

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

482

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

482

2023.08.10

js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

510

2023.06.20

js获取当前时间
js获取当前时间

JS全称JavaScript,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言;它是一种属于网络的高级脚本语言,主要用于Web,常用来为网页添加各式各样的动态功能。js怎么获取当前时间呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

244

2023.07.28

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

258

2023.08.03

AO3中文版入口地址大全
AO3中文版入口地址大全

本专题整合了AO3中文版入口地址大全,阅读专题下面的的文章了解更多详细内容。

1

2026.01.21

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 0.9万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1.0万人学习

PHP自制框架
PHP自制框架

共8课时 | 0.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号