功能清单:

  1. Promise.prototype.then()
  2. Promise.prototype.catch()
  3. Promise.reject()
  4. Promise.resolve()
  5. Promise.all()
  6. Promise.race()
  7. Promise.prototype.finally()

参考:Prmoises A+规范

具体实现

function Promise(fn) {
var _this = this;
var callback = null;
this._value = null; // 存放resolve(解决)的结果
this._reason = null; // 存放reject(拒绝)的原因
this._onResolveds = []; // 存放resolve前调用then时传入的函数onResolved(可能多次调用then,所以是数组)
this._onRejecteds = []; // 存放reject前调用catch时传入的函数onRejected(可能多次调用catch,所以是数组)
this._status = "pending"; function resolve(val) {
if (_this._status !== 'pending') { // 若status已经改变为"resolved"或"rejected",回调直接在then内处理
return
}
_this._value = val;
_this._status = "resolved";
while (callback = _this._onResolveds.shift()) { // 按注册顺序依次执行回调
callback(val)
}
} function reject(reason) {
if (_this._status !== 'pending') {
return
}
_this._reason = reason;
_this._status = "rejected";
while (callback = _this._onRejecteds.shift()) {
callback(reason)
}
} try {
fn(resolve, reject)
} catch (err) {
reject(err)
}
}

then (重点)

  Promise.prototype.then = function (onResolved, onRejected) {
// 规范:如果 onFulfilled 不是函数,其必须被忽略
// 这里,若onResolved, onRejected 不是函数,则用一个过渡性的函数代替
onResolved = typeof onResolved === 'function'? onResolved:function(value) {
return value; // 将value原封不动地传给下一个then,相当于跳过(忽略)本轮then的onResolved
}
onRejected = typeof onRejected === 'function'? onRejected:function(err) {
throw err; // 同上,相当于跳过(忽略)本轮then的onRejected
} var _this = this
var promise2 // then始终返回一个Promise实例,用于链式调用。 if (_this._status === "resolved") {
promise2 = new Promise(function (resolve, reject){
setTimeout(function() { // 确保onResolved 和 onRejected 方法异步执行。下面的setTimeout同理
try {
var x = onResolved(_this._value)
resolvePromise(promise2, x, resolve, reject) // resolvePromise内执行promise2的resolve和reject
} catch (e) {
reject(e)
}
}, 0);
})
}
if (_this._status === "rejected") {
promise2 = new Promise(function (resolve, reject){
setTimeout(function() {
try {
var x = onRejected(_this._reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0);
})
}
if (_this._status === "pending") {
// resolve或reject前调用then的话,将回调推入队列
promise2 = new Promise(function (resolve, reject) {
_this._onResolveds.push(function () {
setTimeout(function() {
try {
var x = onResolved(_this._value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0);
});
_this._onRejecteds.push(function () {
setTimeout(function() {
try {
var x = onRejected(_this._reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0);
}); })
} return promise2
};

根据onResolved/onRejected的返回值 x 的不同情况,调用promise2的resolve和reject

function resolvePromise (promise2, x, resolve, reject) {
if(promise2 === x) { // 防止引用同一个promise,导致死循环
return reject(new TypeError('循环引用'));
}
var called = false // 防止多次调用
if (x!== null && (typeof x ==='object' ||typeof x === 'function')) {
try {
let then = x.then;
if (typeof then === 'function') {
// x 是一个定义了 then 方法的对象或函数,即thenable then.call(x, function(y) { // 这里规范是这样说明的:这步我们先是存储了一个指向 x.then 的引用,然后测试并调用该引用,以避免多次访问 x.then 属性。这种预防措施确保了该属性的一致性,因为其值可能在检索调用时被改变。 if (called) return // 确保resolve和reject,只执行其中一个
called = true;
resolvePromise(promise2, y, resolve, reject) // 如果x是thenable,则继续调用resolvePromise,直到 onResolved/onRejected 的返回值不是 thenable }, function(err) {
if (called) return
called = true;
reject(err);
})
} else {
resolve(x) // 如果 x 不属于 thenable, 则把x作为返回值.
}
} catch (e) {
if (called) return
called = true;
reject(e)
}
} else { //普通值
resolve(x)
}
}
  Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected)
}
  Promise.resolve = function (value) {
return new Promise(function (resolve, reject) {
resolve(value)
})
}
  Promise.reject = function (reason) {
return new Promise(function (resolve, reject) {
reject(reason)
})
}
  Promise.all = function (promises) {
if (!Array.isArray(promises)) {
throw new TypeError('必须传入promise数组');
}
var length = promises.length
var values = [] return new Promise(function (resolve, reject) {
function rejectHandle(reason) {
reject(reason) // 只要其中一个reject,整体reject
} function resolveHandle(index) {
return function (value) {
values[index] = value // 按传入时的顺序记录每个promise的结果值
if (--length === 0) { // 所有子promise都resolve后,整体resolve
resolve(values)
}
}
} promises.forEach(function (item, index) {
item.then(resolveHandle(index), rejectHandle)
})
})
}
  Promise.race = function (promises) {
if (!Array.isArray(promises)) {
throw new TypeError('必须传入promise数组');
}
return new Promise(function (resolve, reject) {
function rejectHandle(reason) {
reject(reason)
} function resolveHandle(value) {
resolve(value)
}
promises.forEach(function (item) {
item.then(resolveHandle, rejectHandle)
})
})
}
  // 不管resolved还是rejected,都会执行,避免同样的语句需要在then()和catch()中各写一次的情况。
Promise.prototype.finally = function (callback) {
return this.then(callback, callback)
}

测试:

使用promises-aplus-tests:全局安装npm i promises-aplus-tests -g,然后命令行 promises-aplus-tests [js文件名] 进行测试

注意:测试前要在尾部加上下面的代码:

Promise.deferred = Promise.defer = function () {
let deferred = {};
deferred.promise = new Promise(function (resolve, reject) {
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred
}; module.exports = Promise

测试完成后可删除

使用 UMD 规范封装:

(function (global, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(factory)
} else if (typeof exports === 'object' && typeof module !== 'undefined') {
// CommonJS (如node)
module.exports = factory()
} else {
// 浏览器全局变量
global.promisePolyfill = factory()
}
})(this, function () {
'use strict';
/*
定义Promise的代码
*/ function promisePolyfill () {
var global = null
try {
global = Function('return this')();
} catch (e) {
throw new Error('全局对象不可用');
}
global.Promise = Promise
}
return promisePolyfill
})

使用

promisePolyfill()  // 注册Promise全局变量

实现自己的Promise polyfill的更多相关文章

  1. IE报vuex requires a Promise polyfill in this browser问题解决

    使用Vuex, IE浏览器报错 因为使用了 ES6 中用来传递异步消息的的Promise,而IE低版本的浏览器不支持. ##解决方法 第一步: 安装 babel-polyfill . babel-po ...

  2. vuex requires a Promise polyfill in this browser

    ie 浏览器访问 vue 项目(使用的vuex 状态管理组件)报错:vuex requires a Promise polyfill in this browser 处理办法: 1.npm insta ...

  3. vue 坑之 vuex requires a Promise polyfill in this browser

    android内嵌H5页面不显示出现这个问题,原因有很多 首先,别急,请看下面的推荐方案: 1.找个Android真机测试下(机型版本为4.4以上),真机联调测试 Android 只需要四个步骤: 1 ...

  4. 解决IE下页面空白或者报错:[vuex] vuex requires a Promise polyfill in this browser

    [vuex] vuex requires a Promise polyfill in this browser 上述错误的原因是不支持 Promise 方法,导致页面出现空白无法加载. 解决方法如下: ...

  5. Error: [vuex] vuex requires a Promise polyfill in this browser. 与 babel-polyfill 的问题

    Error: [vuex] vuex requires a Promise polyfill in this browser. 与 babel-polyfill 的问题 采用最笨重的解决方案就是npm ...

  6. vuex requires a Promise polyfill in this browser.--ie-vue-兼容处理日记

    1.ie9+报错vuex requires a Promise polyfill in this browser. 解决如下: npm install --save-dev -polyfill 修改c ...

  7. vue项目在IE下报 [vuex] vuex requires a Promise polyfill in this browser错误

    ie浏览器下报错 vue刚搭建的项目,在谷歌浏览器能够正常访问,但是在ie11等ie浏览器下无法显示页面,打开控制台查看无报错信息,打开仿真一栏,提示[vuex] vuex requires a Pr ...

  8. IE浏览器报Promise未定义的错误、解决vuex requires a Promise polyfill in this browser问题

    一个vue-cli构建的vue项目,一个使用angular的项目,两个项目在其他浏览器一切正常,但是ie中会报Promise未定义的错误 解决办法: 一.vue的项目: 1.npm install b ...

  9. 解决vuex requires a Promise polyfill in this browser问题

    造成这种现象的原因归根究底就是浏览器对ES6中的promise无法支持,因此需要通过引入babel-polyfill来是我们的浏览器正常使用es6的功能 首先通过npm来安装: npm install ...

  10. IE报错:[vuex] vuex requires a Promise polyfill in this browser.

    使用的是vue2.0版本 IE报错提醒: 导致原因:使用了 ES6 中用来传递异步消息的的Promise,而IE的浏览器不支持 解决办法: 1.安装babel-polyfill模块,babel-plo ...

随机推荐

  1. AC 自动机——多模式串匹配

    网站上的敏感词过滤是怎么实现的呢? 实际上,这些功能最基本的原理就是字符串匹配算法,也就是通过维护一个敏感词的字典,当用户输入一段文字内容后,通过字符串匹配算法来检查用户输入的内容是否包含敏感词. B ...

  2. 关于wcf服务编译平台是x86, 运行平台是x64时,如何调试

    关于调试CTDC项目中的的 wcf服务时注意事项: 因为wcf项目引用的的 x86的程序集,所以wcf生成的目标平台为x86.故在64系统上调试需要执行下面的脚本 具体操作步骤: 1. 必须使用201 ...

  3. windows10下git一些问题

    windows10下安装git 找不到ssh解决办法 解决办法是: 输入下列命令,一路回车 $ ssh-keygen -t rsa -C “邮箱地址” 若执行ssh-add /path/to/xxx. ...

  4. Notes of the scrum meeting before publishing2(12.18)

    meeting time:18:30~20:30p.m.,December 18th,2013 meeting place:3号公寓一层 attendees: 顾育豪                  ...

  5. Alpha 冲刺(1/10)

    队名 火箭少男100 组长博客 林燊大哥 作业博客 Alpha 冲鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调各成员之间的工作,对多个目标检测及文字识别模型进行评估.实验,选取较 ...

  6. Spring学习(三)—— 自动装配案例分析

    Spring_Autowiring collaborators 在Spring3.2.2中自动装配类型,分别为:no(default)(不采用自动装配).byName,byType,construct ...

  7. 【Linux】- 不可不知的小技巧

    1.Tab键:输入文件或目录名的前几个字符,然后按TAB键,如无相重的,完整的文件名立即自动在命令行出现:如有相重的,再按一下TAB键,系统会列出当前目录下所有以这几个字符开头的名字. 在命令行下,只 ...

  8. 织梦dede:list标签在列表页同一文章显示两次的解决方法

    在列表页用{dede:list}标签调用文章的时候出现了同一篇文章显示两次的问题,经过一天的奋战最后终于解决了,下面CMS集中营站长简单说下我的解决过程来供各位学友参考:1.怀疑是不是每次添加都会自动 ...

  9. 简单名称值对节点类NameValuePair

    本类位于System.Data.dll中,名为:System.Data.Common.NameValuePair.主要用途是在DBConnectionString类中,解析ConnectionStri ...

  10. [OS] 操作系统-进程线程-经典面试笔试题

    题目转自:http://blog.csdn.net/morewindows/article/details/7392749 ·线程的基本概念.线程的基本状态及状态之间的关系? 线程,有时称为轻量级进程 ...