Promise 是 ES6 新增的语法,解决了回调地狱的问题。

可以把 Promise 看成一个状态机。初始是 pending 状态,可以通过函数 resolve 和 reject ,将状态转变为 resolved 或者 rejected 状态,状态一旦改变就不能再次变化。

then 函数会返回一个 Promise 实例,并且该返回值是一个新的实例而不是之前的实例。因为 Promise 规范规定除了 pending 状态,其他状态是不可以改变的,如果返回的是一个相同实例的话,多个 then 调用就失去意义了。

对于 then 来说,本质上可以把它看成是 flatMap


// 三种状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// promise 接收一个函数参数,该函数会立即执行
function MyPromise(fn) {
let _this = this;
_this.currentState = PENDING;
_this.value = undefined;
// 用于保存 then 中的回调,只有当 promise
// 状态为 pending 时才会缓存,并且每个实例至多缓存一个
_this.resolvedCallbacks = [];
_this.rejectedCallbacks = []; _this.resolve = function (value) {
if (value instanceof MyPromise) {
// 如果 value 是个 Promise,递归执行
return value.then(_this.resolve, _this.reject)
}
setTimeout(() ={ // 异步执行,保证执行顺序
if (_this.currentState === PENDING) {
_this.currentState = RESOLVED;
_this.value = value;
_this.resolvedCallbacks.forEach(cb =cb());
}
})
}; _this.reject = function (reason) {
setTimeout(() ={ // 异步执行,保证执行顺序
if (_this.currentState === PENDING) {
_this.currentState = REJECTED;
_this.value = reason;
_this.rejectedCallbacks.forEach(cb =cb());
}
})
}
// 用于解决以下问题
// new Promise(() =throw Error('error))
try {
fn(_this.resolve, _this.reject);
} catch (e) {
_this.reject(e);
}
} MyPromise.prototype.then = function (onResolved, onRejected) {
var self = this;
// 规范 2.2.7,then 必须返回一个新的 promise
var promise2;
// 规范 2.2.onResolved 和 onRejected 都为可选参数
// 如果类型不是函数需要忽略,同时也实现了透传
// Promise.resolve(4).then().then((value) =console.log(value))
onResolved = typeof onResolved === 'function' ? onResolved : v =v;
onRejected = typeof onRejected === 'function' ? onRejected : r =throw r; if (self.currentState === RESOLVED) {
return (promise2 = new MyPromise(function (resolve, reject) {
// 规范 2.2.4,保证 onFulfilled,onRjected 异步执行
// 所以用了 setTimeout 包裹下
setTimeout(function () {
try {
var x = onResolved(self.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
});
}));
} if (self.currentState === REJECTED) {
return (promise2 = new MyPromise(function (resolve, reject) {
setTimeout(function () {
// 异步执行onRejected
try {
var x = onRejected(self.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
});
}));
} if (self.currentState === PENDING) {
return (promise2 = new MyPromise(function (resolve, reject) {
self.resolvedCallbacks.push(function () {
// 考虑到可能会有报错,所以使用 try/catch 包裹
try {
var x = onResolved(self.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (r) {
reject(r);
}
}); self.rejectedCallbacks.push(function () {
try {
var x = onRejected(self.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (r) {
reject(r);
}
});
}));
}
};
// 规范 2.3
function resolutionProcedure(promise2, x, resolve, reject) {
// 规范 2.3.1,x 不能和 promise2 相同,避免循环引用
if (promise2 === x) {
return reject(new TypeError("Error"));
}
// 规范 2.3.2
// 如果 x 为 Promise,状态为 pending 需要继续等待否则执行
if (x instanceof MyPromise) {
if (x.currentState === PENDING) {
x.then(function (value) {
// 再次调用该函数是为了确认 x resolve 的
// 参数是什么类型,如果是基本类型就再次 resolve
// 把值传给下个 then
resolutionProcedure(promise2, value, resolve, reject);
}, reject);
} else {
x.then(resolve, reject);
}
return;
}
// 规范 2.3.3.3.3
// reject 或者 resolve 其中一个执行过得话,忽略其他的
let called = false;
// 规范 2.3.3,判断 x 是否为对象或者函数
if (x !== null && (typeof x === "object" || typeof x === "function")) {
// 规范 2.3.3.2,如果不能取出 then,就 reject
try {
// 规范 2.3.3.1
let then = x.then;
// 如果 then 是函数,调用 x.then
if (typeof then === "function") {
// 规范 2.3.3.3
then.call(
x,
y ={
if (called) return;
called = true;
// 规范 2.3.3.3.1
resolutionProcedure(promise2, y, resolve, reject);
},
e ={
if (called) return;
called = true;
reject(e);
}
);
} else {
// 规范 2.3.3.4
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
// 规范 2.3.4,x 为基本类型
resolve(x);
}
}

以上就是根据 Promise / A+ 规范来实现的代码,可以通过 promises-aplus-tests 的完整测试

参考资料:

前端进阶之道

promise实现的更多相关文章

  1. Javascript - Promise学习笔记

    最近工作轻松了点,想起了以前总是看到的一个单词promise,于是耐心下来学习了一下.   一:Promise是什么?为什么会有这个东西? 首先说明,Promise是为了解决javascript异步编 ...

  2. 路由的Resolve机制(需要了解promise)

    angular的resovle机制,实际上是应用了promise,在进入特定的路由之前给我们一个做预处理的机会 1.在进入这个路由之前先懒加载对应的 .js $stateProvider .state ...

  3. angular2系列教程(七)Injectable、Promise、Interface、使用服务

    今天我们要讲的ng2的service这个概念,和ng1一样,service通常用于发送http请求,但其实你可以在里面封装任何你想封装的方法,有时候控制器之间的通讯也是依靠service来完成的,让我 ...

  4. 闲话Promise机制

    Promise的诞生与Javascript中异步编程息息相关,js中异步编程主要指的是setTimout/setInterval.DOM事件机制.ajax,通过传入回调函数实现控制反转.异步编程为js ...

  5. 深入理解jQuery、Angular、node中的Promise

    最初遇到Promise是在jQuery中,在jQuery1.5版本中引入了Deferred Object,这个异步队列模块用于实现异步任务和回调函数的解耦.为ajax模块.队列模块.ready事件提供 ...

  6. Promise的前世今生和妙用技巧

    浏览器事件模型和回调机制 JavaScript作为单线程运行于浏览器之中,这是每本JavaScript教科书中都会被提到的.同时出于对UI线程操作的安全性考虑,JavaScript和UI线程也处于同一 ...

  7. JavaScript进阶之路——认识和使用Promise,重构你的Js代码

    一转眼,这2015年上半年就过去了,差不多一个月没有写博客了,"罪过罪过"啊~~.进入了七月份,也就意味着我们上半年苦逼的单身生活结束了,从此刻起,我们要打起十二分的精神,开始下半 ...

  8. 细说Promise

    一.前言 JavaScript是单线程的,固,一次只能执行一个任务,当有一个任务耗时很长时,后面的任务就必须等待.那么,有什么办法,可以解决这类问题呢?(抛开WebWorker不谈),那就是让代码异步 ...

  9. 浅谈Angular的 $q, defer, promise

    浅谈Angular的 $q, defer, promise 时间 2016-01-13 00:28:00  博客园-原创精华区 原文  http://www.cnblogs.com/big-snow/ ...

  10. angular学习笔记(二十八-附2)-$http,$resource中的promise对象

    下面这种promise的用法,我从第一篇$http笔记到$resource笔记中,一直都有用到: HttpREST.factory('cardResource',function($resource) ...

随机推荐

  1. 原创:自定义spark GraphX中的collectNeighborIds方法

    /** * 自定义收集VertexId的neighborIds * @author TongXueQiang */def collectNeighborIds[T,U](edgeDirection:E ...

  2. 【06NOIP普及组】数列(信息学奥赛一本通 1937)(洛谷 1062)

    [题目描述] 给定一个正整数k(3≤k≤15),把所有k的方幂及所有有限个互不相等的k的方幂之和构成一个递增的序列,例如,当k=3时,这个序列是: 1,3,4,9,10,12,13,… (该序列实际上 ...

  3. 前端base64加密

    一.Base64编码表 码值 字符 码值 字符 码值 字符 码值 字符 0 A 16 Q 32 g 48 w 1 B 17 R 33 h 49 x 2 C 18 S 34 i 50 y 3 D 19 ...

  4. 多线程 fork/join 并行计算

    1. 什么是Fork/Join框架 Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架. 我们再通过 ...

  5. Goroutine调度器

    前言 并发(并行)一致都是编程语言的核心主题,不同于其他语言,例如C/C++语言用户序自行借助pthread创建线程,Golang天然就给出了并发解决方案:goroutine. Goroutine 写 ...

  6. react项目如何运行

    react项目如何运行 一.总结 一句话总结: npm i 安装好package.json的 指定插件后,npm start 启动项目 二.react项目的安装与运行 转自或参考:react项目的安装 ...

  7. Java文件上传下载原理

    文件上传下载原理 在TCP/IP中,最早出现的文件上传机制是FTP.它是将文件由客户端发送到服务器的标准机制. 但是在jsp编程中不能使用FTP方法来上传文件,这是由jsp运行机制所决定的 文件上传原 ...

  8. docker vim右键进入visual模式无法粘贴

    右键不能粘贴,反而进入了visual模式, vim版本:version 8.0.707 修改方法: vim /usr/share/vim/vim80/defaults.vim 第70行 在mouse= ...

  9. 解决Electron安装包下载慢的问题

    问题描述 在运行官网示例的过程中,一个安装包(electron-v5.0.8-darwin-x64.zip)下载特别慢,基本下载不下来.更改npm的安装源到taobao的镜像,也没有效果. 解决方案 ...

  10. Charles 激活入口以及账号密码

    激活入口 // Charles Proxy License // 适用于Charles任意版本的注册码,谁还会想要使用破解版呢. // Charles 4.2目前是最新版,可用. Registered ...