文档章节

Promise原理

gtandsn
 gtandsn
发布于 2019/10/24 19:28
字数 1104
阅读 28
收藏 0

简介

Promise 对象用于延迟(deferred) 计算和异步(asynchronous )计算。一个Promise对象代表着一个还未完成,但预期将来会完成的操作。Promise 对象是一个返回值的代理,这个返回值在promise对象创建时未必已知。它允许你为异步操作的成功或失败指定处理方法。 这使得异步方法可以像同步方法那样返回值:异步方法会返回一个包含了原返回值的 promise 对象来替代原返回值。

解决了什么问题及怎么使用

// 一个简单的示例 执行一个动画A,执行完之后再去执行另一个动画B
setTimeout(function(){
    //A动画
    console.log('A');
    setTimeout(function() {
        //B动画
        console.log('B');
    },300)
},300);
// 这里只有两个动画,如果有更多呢,就会看到一堆函数缩进

不难想象,如果依次有很多个动画,就会出现多重嵌套。代码不是纵向发展,而是横向发展,很快就会乱成一团,无法管理。

因为多个异步操作形成了强耦合,只要有一个操作需要修改,它的上层回调函数和下层回调函数,可能都要跟着修改。这种情况就称为回调函数地狱“(callback hell)

Promise 对象就是为了解决这个问题而提出的。它不是新的语法功能,而是一种新的写法,允许将回调函数的嵌套,改成链式调用。 

浏览器实现方式:可以在支持Promise的版本上运行

var p = new Promise(function (resolve, reject) {
    setTimeout(function () {
        // A动画
        console.log('A');
        resolve();
    }, 300);
});

p.then(function () {
    setTimeout(function () {
        // B动画
        console.log('B');
    }, 300);
});

promise会让代码变得更容易维护,像写同步代码一样写异步代码。

promise原理

其实,promise就是三个状态。利用观察者模式的编程思想,只需要通过特定书写方式注册对应状态的事件处理函数,然后更新状态,调用注册过的处理函数即可。 

这个特定方式就是then,done,fail,always…等方法,更新状态就是resolve、reject方法。

/**
 * Promise类实现原理
 * 构造函数传入一个function,有两个参数,resolve:成功回调; reject:失败回调
 * state: 状态存储 [PENDING-进行中 RESOLVED-成功 REJECTED-失败]
 * doneList: 成功处理函数列表
 * failList: 失败处理函数列表
 * done: 注册成功处理函数
 * fail: 注册失败处理函数
 * then: 同时注册成功和失败处理函数
 * always: 一个处理函数注册到成功和失败
 * resolve: 更新state为:RESOLVED,并且执行成功处理队列
 * reject: 更新state为:REJECTED,并且执行失败处理队列
**/

class PromiseNew {
  constructor(fn) {
    this.state = 'PENDING';
    this.doneList = [];
    this.failList = [];
    fn(this.resolve.bind(this), this.reject.bind(this));
  }

  // 注册成功处理函数
  done(handle) {
    if (typeof handle === 'function') {
      this.doneList.push(handle);
    } else {
      throw new Error('缺少回调函数');
    }
    return this;
  }

  // 注册失败处理函数
  fail(handle) {
    if (typeof handle === 'function') {
      this.failList.push(handle);
    } else {
      throw new Error('缺少回调函数');
    }
    return this;
  }

  // 同时注册成功和失败处理函数
  then(success, fail) {
    this.done(success || function () { }).fail(fail || function () { });
    return this;
  }

  // 一个处理函数注册到成功和失败
  always(handle) {
    this.done(handle || function () { }).fail(handle || function () { });
    return this;
  }

  // 更新state为:RESOLVED,并且执行成功处理队列
  resolve() {
    this.state = 'RESOLVED';
    let args = Array.prototype.slice.call(arguments);
    setTimeout(function () {
      this.doneList.forEach((item, key, arr) => {
        item.apply(null, args);
        arr.shift();
      });
    }.bind(this), 200);
  }

  // 更新state为:REJECTED,并且执行失败处理队列
  reject() {
    this.state = 'REJECTED';
    let args = Array.prototype.slice.call(arguments);
    setTimeout(function () {
      this.failList.forEach((item, key, arr) => {
        item.apply(null, args);
        arr.shift();
      });
    }.bind(this), 200);
  }
}

// 下面一波骚操作
new PromiseNew((resolve, reject) => {
  resolve('hello world');
  // reject('you are err');
}).done((res) => {
  console.log(res);
}).fail((res) => {
  console.log(res);
})

解释:当我们调用new Promise(fn)时,就会立即执行第一个参数fn。上面案例中,先将then(done,fail)对应的回调函数分别加入到doneList或者failList中,always中对应的参数同时加到doneList和failList中;

当异步代码执行完毕,就调用resolve方法,此时改变promise的状态为resolved,并执行doneList里面的回调函数。如果执行失败,则调用fail方法,此时改变promise的状态为rejected,并执行failList里面的回调函数。

© 著作权归作者所有

上一篇: 有用的话
下一篇: 面试总结
gtandsn
粉丝 0
博文 121
码字总数 54095
作品 0
成都
私信 提问
自己手撸一个符合Promise/A+的Promise

简单介绍 Promise是什么,相信不用说了,写过js的人或多或少都接触过。刚开始用Promise的时候,总感觉这种写法非常的怪异,但是当慢慢熟悉的时候,发现一切都是那么和谐。 我自己理解的Promi...

Xiaowei
2018/09/04
0
0
这一次,彻底弄懂 Promise 原理

Promise 必须为以下三种状态之一:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。一旦Promise 被 resolve 或 reject,不能再迁移至其他任何状态(即状态 immutable)。 基本...

winty
2019/09/04
0
0
Promise实现的基本原理(一)

前言 前几天在项目中遇到回调地狱的情况,但我在刚开始写这个项目的时候还不会用Promise,只知道它可以用来解决回调地狱,所以就用全都用回调函数解决。我是看《深入理解ES6》这本书来学Pro...

Britta
2019/04/14
0
0
【源码系列】redux-chunk、redux-promise 源码分析

源码系列: 【源码系列】react-router v5.x 源码分析和理解 SPA router 的原理 【源码系列】redux v4.x 源码解析 上一篇文章里,action 都是同步的,也就是说 dispatch(action),经过中间件,...

hankzhuo
2019/07/12
0
0
JavaScript 异步

JavaScript怎么使用循环代替(异步)递归 问题描述 在开发过程中,遇到一个需求:在系统初始化时通过http获取一个第三方服务器端的列表,第三方服务器提供了一个接口,可通过分页形式获取列表。...

掘金官方
2018/01/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

CentOS-启用SFTP

创建用户组及用户 $ groupadd sftp $ useradd -g sftp -s /sbin/nologin -d /home/sftp sftp 设置密码 $ passwd sftp 输入密码(123456) 确认密码 修改sshd_config文件 $ vim /etc/ssh/sshd_......

自由人生-ZYRS
15分钟前
11
0
这个IM项目没时间搞了,开源算了。10万并发,基于golang。

先上效果 安装方法 本系统升级到golang1.12,请开启如下支持 #开启go mod支持export GO111MODULE=on#使用代理export GOPROXY=https://goproxy.io 1.下载项目 git clone https://github.c...

非正式解决方案
19分钟前
6
0
Mysql基本操作

查看mysql中已经有的数据库 二、删除已经有的数据库school 三、创建新数据库myschool 四、进入到myschool中 五、查看myschool库中所有的表 六、新建一张student表 七、查看student表结构 八、...

愚蠢的土豆
19分钟前
8
0
经典检索算法:BM25

BM25算法是一种常见用来做相关度打分的公式 思路比较简单,主要就是计算一个query里面所有词和文档的相关度, 然后在把分数做累加操作 而每个词的相关度分数主要还是受到tf/idf的影响 其实就...

Java搬砖工程师
26分钟前
5
0
详解mycat+haproxy+keepalived搭建高可用负载均衡mysql集群

概述 目前业界对数据库性能优化普遍采用集群方式,而oracle集群软硬件投入昂贵,mysql则比较推荐用mycat去搭建数据库集群,下面介绍一下怎么用mycat+haproxy+keepalived搭建一个属于mysql数据...

小致Daddy
26分钟前
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部