2015年6月,ES2015(即ES6)正式发布后受到了非常多的关注。其中很重要的一点是 Promise 被列为了正式规范。
在此之前很多库都对异步编程/回调地狱实现了类 Promise 的应对方案,比如 bluebird、Angular 的 Q 和大名鼎鼎的 jQuery 的 deffered 等。

为了便于理解,本文将分为三个部分,每个部分实现 Promise 的一部分特性,最终一步一步的实现一个完整的、遵循 promise A+ 规范的 Promise。

Promise A+规范规定,Promise 有三种状态,分别是 pending(默认状态,代表等待)、fulfilled(代表成功)、rejected(代表失败)。

来看看 Promise 的用法,以及它有哪些特性。
var fn = new Promise(function (resolve, reject) {
// 异步操作
setTimeout(function() {
resolve('resolve 01')
// 由于reslove和 reject 是互斥的,因为已经调用了 resolve,这里reject不会执行
reject('reject 01')
}, 500)
})
fn().then(function (data) {
console.log('成功1:', data)
return new Promise(function (resolve, reject) {
reject('reject 02')
}
)}, function (err) {
console.log('失败1:', err)
})
.then(function (data) {
console.log('成功2:', data)
}, function (err) {
console.log('失败2:', err)
}).then(function (data) {
console.log('成功2:', data)}, function (err) {
console.log('失败2:', err)
})

结果:

可以看到,Promise 通常会有这些特性:

①、Promise 类有 then 方法,then 方法有两个参数,分别是 Promise 成功和失败的回调,且二者互斥,调用了其中一个,另外一个就不会执行

②、Promise 支持链式调用,then 的返回值可以是一个 Promise,也可以是一个普通值,如果是 一个普通的值,那么就会当做下一个 then 成功的回调函数的参数

③、Promis 还有其它扩展方法

说了一堆有的没的,下面就开始来实现这个东西吧~

=================================================

一,Promise 类的实现

=> Promise 有 then 方法:
var Promise = function (executor) {
console.log('证明不是用的原生 Promise')
var _this = this
this.status = 'pending'
// 默认状态,可以转化为 resolved 和 rejected
this.successVal = undefined
this.failVal = undefined // 执行了成功或失败后,要将状态对应修改成成功和失败
function resolve (val) {
if ( _this.status === 'pending' ) {
_this.status = 'resolved'
_this.successVal = val
}
}
function reject (val) {
if ( _this.status === 'pending' ) {
_this.status = 'rejected'
_this.failVal = val
}
} try {
// 应该还记得,Promise 的参数是一个函数吧,我们称之为 executor(执行器)
// 同时这个函数接收2个参数,分别是成功和失败的回调函数
executor(resolve, reject)
} catch (e) {
// 如果发生异常,直接reject捕获
reject(e)
}
}
// then 方法接收2个参数,分别是成功和失败的回调函数
Promise.prototype.then = function (onFulfilled, onRejected) {
var _this = this
// 显然要根据当前状态来执行成功或失败的回调了
if ( _this.status === 'resolved' ) {
onFulfilled(_this.successVal)
}
if ( _this.status === 'rejected' ) {
onFulfilled(_this.failVal)
}
}
来试试效果:
var fn = new Promise(function (resolve, reject) {
resolve('oooook~')
})
fn.then(function (data) {
console.log('success: ', data)}, function (err) {
console.log('err: ', err)
})

结果:

结果看上去很美。但如果改成下面这样呢?

var fn = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('oooook~')
}, 500)
})
fn.then(function (data) {
console.log('success: ', data)
}, function (err) {
console.log('err: ', err)
})

结果:

ok,问题来了,Promise 费大力气搞出来可不是只能做同步调用的,毕竟有了 ajax 之后前端才会发展到今天这么繁荣,所以需要让我们的 Promise 支持异步。

修改如下(注释内是对新增代码的说明):
var Promise = function (executor) {
console.log('证明不是用的原生 Promise 的一句废话')
var _this = this this.status = 'pending'
// 默认状态,可以转化为 resolved 和 rejected
this.successVal = undefined
this.failVal = undefined
// ----------------- 表示新增代码 -------------------- // 用于存放成功和失败的回调
this.onFulfilledList = []
this.onRejectedList = [] function resolve (val) {
if ( _this.status === 'pending' ) {
_this.status = 'resolved'
_this.successVal = val
// -------------- 执行所有的成功回调 ---------------
_this.onFulfilledList.forEach(function(fn){
fn()
})
}
}
function reject (val) {
if ( _this.status === 'pending' ) {
_this.status = 'rejected'
_this.failVal = val
// -------------- 执行所有的失败回调 ---------------
_this.onRejectedList.forEach(function(fn){
fn()
})
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
Promise.prototype.then = function (onFulfilled, onRejected) { var _this = this if ( _this.status === 'resolved' ) {
onFulfilled(_this.successVal)
}
if ( _this.status === 'rejected' ) {
onFulfilled(_this.failVal)
} // ----------------- 对异步调用的处理 -------------------
// 说明:如果是异步调用,走到 then 方法里的时候,status 还没有被修改,仍然
// 是 pending 状态,所以这时候需要再回去执行 executor 里的对应方法,而对应的
// 方法会将对应的存放回调的 list 里的方法执行(类似发布-订阅模式一样的处理)
if ( _this.status === 'pending' ) {
_this.onFulfilledList.push(function () {
onFulfilled(_this.successVal)
})
_this.onRejectedList.push(function () {
onRejected(_this.failVal)
})
}
}

看看效果:

目前来看已经支持异步了~

听说你们都在黑我大星爵?星爵那么萌,你们忍心吗

下一篇:一步一步来实现一个Promise A+规范的 Promise之二:Promise 链式调用

一步一步实现一个Promise A+规范的 Promise的更多相关文章

  1. 手写一款符合Promise/A+规范的Promise

    手写一款符合Promise/A+规范的Promise 长篇预警!有点长,可以选择性观看.如果对Promise源码不是很清楚,还是推荐从头看,相信你认真从头看到尾,并且去实际操作了,肯定会有收获的.主要 ...

  2. 手写符合Promise/A+规范的Promise

    const PENDING = "pending"; const RESOLVED = "resolved"; const REJECTED = "r ...

  3. 手写基于Promise A+规范的Promise

    const PENDING = 'pending';//初始态const FULFILLED = 'fulfilled';//初始态const REJECTED = 'rejected';//初始态f ...

  4. 按照 Promise/A+ 规范逐行注释并实现 Promise

    0. 前言 面试官:「你写个 Promise 吧.」 我:「对不起,打扰了,再见!」 现在前端越来越卷,不会手写 Promise 都不好意思面试了(手动狗头.jpg).虽然没多少人会在业务中用自己实现 ...

  5. Promise原理—一步一步实现一个Promise

    promise特点 一个promise的当前状态只能是pending.fulfilled和rejected三种之一.状态改变只能是pending到fulfilled或者pending到rejected ...

  6. 剖析Promise内部结构,一步一步实现一个完整的、能通过所有Test case的Promise类

    本文写给有一定Promise使用经验的人,如果你还没有使用过Promise,这篇文章可能不适合你,建议先了解Promise的使用 Promise标准解读 1.只有一个then方法,没有catch,ra ...

  7. 教你一步一步实现一个Promise

    Promise我想现在大家都非常熟悉了,主要作用就是解决异步回调问题,这里简单介绍下. Promise规范是CommonJS规范之一,而Promise规范又分了好多种,比如 Promises/A.Pr ...

  8. 如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑

    阅读目录 前言 场景1的思考 场景2的思考 避坑方式 实践 结语 一.前言 在上一篇中(如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成),有一行注释的代码: public interfa ...

  9. 如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成

    阅读目录 前言 建模 实现 结语 一.前言 前面几篇已经实现了一个基本的购买+售价计算的过程,这次再让售价丰满一些,增加一个会员价的概念.会员价在现在的主流电商中,是一个不大常见的模式,其带来的问题是 ...

随机推荐

  1. opencv 一些函数的耗时计算

    Release 模式 -------------------------------------------------- smooth gaussian : 2 cvtColor CV_BGR2La ...

  2. 转:GitHub团队项目合作流程

    转自:https://www.cnblogs.com/schaepher/p/4933873.html GitHub团队项目合作流程   已在另一篇博客中写出关于以下问题的解决,点此进入: 同步团队项 ...

  3. 原生JS-实现轮播图

    用原生JS实现一个轮播图(效果) HTML <div id="outer"> <ul id="imgList"> <!-- 图片列 ...

  4. 记录FTPClient超时处理的相关问题(转)

    https://www.cnblogs.com/dasusu/p/10006899.html 记录 FTPClient 超时处理的相关问题   apache 有个开源库:commons-net,这个开 ...

  5. kbmMemTable中怎么根据UniqueRecID定位到对应的记录

    function TForm5.LocateUniqueRecID(aDataSet: TkbmMWCustomClientQuery; AID: TkbmNativeInt): Boolean; v ...

  6. 使用Django开发简单接口:文章增删改查

    目录 1.一些准备工作 安装django 创建django项目 创建博客应用(app) 2.models.py 3.django admin 登录 创建超级用户 4.修改urls.py 5.新增文章接 ...

  7. shell脚本获取传递的参数

    1 脚本编写 #!/bin/bash 2 解释 $n 表示是第几个参数 $0 表示脚本命令本身 3 执行效果

  8. ISO/IEC 15444-12 MP4 封装格式标准摘录 5

    目录 Segments Segment Type Box Segment Index Box Subsegment Index Box Producer Reference Time Box Supp ...

  9. 理解JavaScript里的 [].forEach.call() 写法

    原文:  http://www.webhek.com/javascript-foreach-call document.querySelectorAll() 返回的并不是我们想当然的数组,而是 Nod ...

  10. java线程基础巩固---线程ID,优先级

    这里学习Thread的两个比较简单的API,直接上代码: 线程ID: 那它的生成规则是?直接看源码: 那为什么目前打印是9呢?然后在jvm启动的时候就已经创建了8个线程?继续用jconsole来验证一 ...