1.promise的含义

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise对象有以下两个特点。

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

2.小试promise用法:

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

var p=new Promise(function(resolve,rejcet){setTimeout(function(){if(true){//异步操作成功resolve('success');}else{//异步操作失败rejcet('failure');}},1000);
});
p.then(function(value){//成功的回调console.log(value);
},function(error){//失败的回调console.log(error);
});复制代码

从demo中可以看到,在ES6中Promise对象是一个构造函数,用来生成Promise实例。并且Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

3.手写一个promise,彻底掌握它的原理

首先,我们先搭建好代码的骨架:

function Promise(callback) {var self = this //promise实例self.status = 'PENDING' // Promise当前的状态self.data = undefined  // Promise的值self.onResolvedCallback = [] // Promise resolve时的回调函数集self.onRejectedCallback = [] // Promise reject时的回调函数集callback(resolve, reject) // 执行executor并传入相应的参数function resolve(value){}function reject(error){}
}
// 添加我们的then方法
Promise.prototype.then=function(){}复制代码

我们先创建一个Promise构造函数,并传入一个回调函数callback,callback里面传入两个函数作为参数,一个是resove,一个是reject。并在Promise的原型上加入我们的then方法。框架搭好了,接下来我们来一点点的完善框架里面的内容,可以这么说,把resolve,reject和then补充完,基本可以说就是把Promise完成了。

然后紧接着,我们完善一下resolve和reject:

function Promise(callback) {var self = thisself.status = 'PENDING' // Promise当前的状态self.data = undefined  // Promise的值self.onResolvedCallback = [] // Promise resolve时的回调函数集self.onRejectedCallback = [] // Promise reject时的回调函数集callback(resolve, reject) // 执行executor并传入相应的参数function resolve(value){if(self.status=='PENDING'){self.status=='FULFILLED';self.data=value;// 依次执行成功之后的函数栈for(var i = 0; i < self.onResolvedCallback.length; i++) {self.onResolvedCallback[i](value)}}}function rejecte(error){if (self.status === 'PENDING') {self.status = 'REJECTED'self.data = error;// 依次执行失败之后的函数栈for(var i = 0; i < self.onRejectedCallback.length; i++) {self.onRejectedCallback[i](error)}}}
}复制代码

接下来我们实现我们的then方法:

then方法是Promise的核心,因此这里会花比较大的篇幅去介绍then:

一个promise的then接受两个参数:

promise.then(onFulfilled, onRejected)复制代码

onFulfilledonRejected 都是可选参数。

  • 如果 onFulfilled 不是函数,其必须被忽略
  • 如果 onRejected 不是函数,其必须被忽略

onFulfilled 特性

如果 onFulfilled 是函数:

  • promise 执行结束后其必须被调用,其第一个参数为 promise 的终值,也就是resolve传过来的值
  • promise 执行结束前其不可被调用
  • 其调用次数不可超过一次

onRejected 特性

如果 onRejected 是函数:

  • promise 被拒绝执行后其必须被调用,其第一个参数为 promise 的据因,也就是reject传过来的值
  • promise 被拒绝执行前其不可被调用
  • 其调用次数不可超过一次

调用时机

onFulfilledonRejected 只有在执行环境堆栈仅包含平台代码时才可被调用(平台代码指引擎、环境以及 promise 的实施代码)

调用要求

onFulfilledonRejected 必须被作为函数调用(即没有 this 值,在严格模式(strict) 中,函数 this 的值为 undefined ;在非严格模式中其为全局对象。)

多次调用

then 方法可以被同一个 promise 调用多次

  • promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调
  • promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调

返回

then 方法必须返回一个 promise 对象

promise2 = promise1.then(onFulfilled, onRejected);复制代码

  • 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程[[Resolve]](promise2, x)
  • 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
  • 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
  • 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因

不论 promise1 被 reject 还是被 resolve 时 promise2 都会被 resolve,只有出现异常时才会被 rejected

每个Promise对象都可以在其上多次调用then方法,而每次调用then返回的Promise的状态取决于那一次调用then时传入参数的返回值,所以then不能返回this,因为then每次返回的Promise的结果都有可能不同。

接下来我们来写我们的then方法:

Promise.prototype.then = function(onResolved, onRejected) {var self = thisvar promise2// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理onResolved = typeof onResolved === 'function' ? onResolved : function(value) {}onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {}if (self.status === 'resolved') {// 如果promise1(此处即为this/self)的状态已经确定并且是resolved,我们调用onResolved// 因为考虑到有可能throw,所以我们将其包在try/catch块里return promise2 = new Promise(function(resolve, reject) {try {var x = onResolved(self.data)if (x instanceof Promise) { // 如果onResolved的返回值是一个Promise对象,直接取它的结果做为promise2的结果x.then(resolve, reject)}resolve(x) // 否则,以它的返回值做为promise2的结果} catch (e) {reject(e) // 如果出错,以捕获到的错误做为promise2的结果}})}// 此处与前一个if块的逻辑几乎相同,区别在于所调用的是onRejected函数,就不再做过多解释if (self.status === 'rejected') {return promise2 = new Promise(function(resolve, reject) {try {var x = onRejected(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})}if (self.status === 'pending') {// 如果当前的Promise还处于pending状态,我们并不能确定调用onResolved还是onRejected,// 只能等到Promise的状态确定后,才能确实如何处理。// 所以我们需要把我们的**两种情况**的处理逻辑做为callback放入promise1(此处即this/self)的回调数组里// 逻辑本身跟第一个if块内的几乎一致,此处不做过多解释return promise2 = new Promise(function(resolve, reject) {self.onResolvedCallback.push(function(value) {try {var x = onResolved(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})self.onRejectedCallback.push(function(reason) {try {var x = onRejected(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})})}
}// 为了下文方便,我们顺便实现一个catch方法
Promise.prototype.catch = function(onRejected) {return this.then(null, onRejected)
}复制代码

最后附上完整的代码:

Promise.prototype.then = function(onResolved, onRejected) {var self = thisvar promise2// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理onResolved = typeof onResolved === 'function' ? onResolved : function(value) {}onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {}if (self.status === 'resolved') {// 如果promise1(此处即为this/self)的状态已经确定并且是resolved,我们调用onResolved// 因为考虑到有可能throw,所以我们将其包在try/catch块里return promise2 = new Promise(function(resolve, reject) {try {var x = onResolved(self.data)if (x instanceof Promise) { // 如果onResolved的返回值是一个Promise对象,直接取它的结果做为promise2的结果x.then(resolve, reject)}resolve(x) // 否则,以它的返回值做为promise2的结果} catch (e) {reject(e) // 如果出错,以捕获到的错误做为promise2的结果}})}// 此处与前一个if块的逻辑几乎相同,区别在于所调用的是onRejected函数,就不再做过多解释if (self.status === 'rejected') {return promise2 = new Promise(function(resolve, reject) {try {var x = onRejected(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})}if (self.status === 'pending') {// 如果当前的Promise还处于pending状态,我们并不能确定调用onResolved还是onRejected,// 只能等到Promise的状态确定后,才能确实如何处理。// 所以我们需要把我们的**两种情况**的处理逻辑做为callback放入promise1(此处即this/self)的回调数组里// 逻辑本身跟第一个if块内的几乎一致,此处不做过多解释return promise2 = new Promise(function(resolve, reject) {self.onResolvedCallback.push(function(value) {try {var x = onResolved(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})self.onRejectedCallback.push(function(reason) {try {var x = onRejected(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})})}
}// 为了下文方便,我们顺便实现一个catch方法
Promise.prototype.catch = function(onRejected) {return this.then(null, onRejected)
}复制代码

用es5实现es6的promise,彻底搞懂promise的原理相关推荐

  1. ES6学习——一文搞懂ES6

    ES6学习--一文搞懂ES6 es6介绍 ES全称EcmaScript,是脚本语言的规范,而平时经常编写的EcmaScript的一种实现,所以ES新特性其实就是指JavaScript的新特性. 为什么 ...

  2. php service原理,轻松搞懂WebService工作原理

    用更简单的方式给大家谈谈WebService,让你更快更容易理解,希望对初学者有所帮助. WebService是基于网络的.分布式的模块化组件. 我们直接来看WebService的一个简易工作流程: ...

  3. Android 动态分区详解(一) 5 张图让你搞懂动态分区原理

    文章目录 0. 导读 1. 动态分区详解的背景 1.1 背景 1.2 动态分区的本质 2. Linux device mapper 驱动 3. Android 动态分区布局 3.1 动态分区布局 3. ...

  4. 【前端知识点】promise简书-30分钟带你搞懂promise面试必备

    前言 写作初衷 本书的目的是以目前还在制定中的ECMAScript 6 Promises规范为中心,着重向各位读者介绍JavaScript中对Promise相关技术的支持情况. 通过阅读本书,我们希望 ...

  5. 这次彻底搞懂 Promise(手写源码多注释篇)

    作者:一阵风,一枚只想安静写代码的程序员,来自程序员成长指北交流群    github: https://github.com/yizhengfeng-jj/promise 前言 promise 是 ...

  6. 异步编程:一次搞懂Promise,async,await

    文章目录 前言 一.回调函数 二.Promise 三.错误处理 四.async/await await使用时的陷阱 1 2 3 总结 前言 异步编程允许我们在执行一个长时间任务时,程序不需要进行等待, ...

  7. 一首歌带你搞懂Promise

    Promise 是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和更强大.(摘自<ECMAScript 6 入门>),但是对于Promise的创建和执行流程,其实有很 ...

  8. 别不承认!搞懂那些数理原理,才发现它们和枯燥根本不沾边!

    ▲ 点击查看 数理化的学习对于很多孩子,包括家长都是一个大难题. 比如,我们要教孩子认识动物,一般是要给孩子看动物的图片或实体,孩子自然就对这个动物有个认知. 要教孩子数字,就会用一件玩具.两个苹果这 ...

  9. reactrouter监听路由变化_一篇文章搞懂前端路由原理解析和实现方式

    在单页应用如此流行的今天,曾经令人惊叹的前端路由已经成为各大框架的基础标配,每个框架都提供了强大的路由功能,导致路由实现变的复杂. 想要搞懂路由内部实现还是有些困难的,但是如果只想了解路由实现基本原理 ...

最新文章

  1. 【云计算】5_云存储产品介绍
  2. java multivaluemap_java – 使用自定义值集合类型创建Commons Collections MultiValueMap
  3. Python基础day08 作业解析【7道 面向对象题目】
  4. Unity3d Fast Indirect illumination Using Two Virtual Spherical Gaussian Lights-Square Enix论文
  5. 几种常用的优化方法梯度下降法、牛顿法、)
  6. Sql Server2005对t-sql的增强之Cross Apply
  7. 发一个多CPU中进程与CPU绑定的例子
  8. Java正则速成秘籍(一)之招式篇
  9. 汇编学习--6.13--基础知识
  10. vue-router学习第一天
  11. 使用 MyEclipse远程调试 Java 应用程序
  12. 手机远程控制电脑方法(TeamViewer vs 向日葵)
  13. 计算机网络——模拟信号(四)
  14. 阿里淘系优质开源项目推荐(下)
  15. 用CSS画小猪佩奇,你就是下一个社会人!
  16. 【深度域自适应】DANN梯度反转层(GRL)理解
  17. 基于SSH的校园二手物品交易系统
  18. 利用百度网盘引流方法,如何利用百度网盘进行精准引流?
  19. It's a test
  20. LayIM 3.9.1与ASP.NET SignalR实现Web聊天室快速入门(七)之LayIM与MVC数据交互实现单聊和群聊

热门文章

  1. 想学IT的必看!深度解析跳槽从开始到结束完整流程万字长文!
  2. 计算机科学渗透信息论的思想,信息系统思想在高中地理课程教学中的渗透方法分析...
  3. java多线程安全解决方案_《Java多线程编程核心技术(第2版)》 —1.2.8 实例变量共享造成的非线程安全问题与解决方案...
  4. 构造java_Java构造方法
  5. 机器人绳索英雄下载苹果手机_机器人绳索英雄
  6. 判断php对象不包含的key,如何判断一个对象是否包含指定Key
  7. Vue+Vue Router+Webpack打包网站基础页面
  8. 我的世界服务器不显示浮空字,我的世界服务器浮空字怎么做 | 手游网游页游攻略大全...
  9. html保存为svg,JavaScript – 将内联SVG保存为JPEG / PNG / SVG
  10. python同时发大量请求_Python批量发送post请求的实现代码