功能清单:

  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+规范

具体实现

  1. function Promise(fn) {
  2. var _this = this;
  3. var callback = null;
  4. this._value = null; // 存放resolve(解决)的结果
  5. this._reason = null; // 存放reject(拒绝)的原因
  6. this._onResolveds = []; // 存放resolve前调用then时传入的函数onResolved(可能多次调用then,所以是数组)
  7. this._onRejecteds = []; // 存放reject前调用catch时传入的函数onRejected(可能多次调用catch,所以是数组)
  8. this._status = "pending";
  9. function resolve(val) {
  10. if (_this._status !== 'pending') { // 若status已经改变为"resolved"或"rejected",回调直接在then内处理
  11. return
  12. }
  13. _this._value = val;
  14. _this._status = "resolved";
  15. while (callback = _this._onResolveds.shift()) { // 按注册顺序依次执行回调
  16. callback(val)
  17. }
  18. }
  19. function reject(reason) {
  20. if (_this._status !== 'pending') {
  21. return
  22. }
  23. _this._reason = reason;
  24. _this._status = "rejected";
  25. while (callback = _this._onRejecteds.shift()) {
  26. callback(reason)
  27. }
  28. }
  29. try {
  30. fn(resolve, reject)
  31. } catch (err) {
  32. reject(err)
  33. }
  34. }

then (重点)

  1. Promise.prototype.then = function (onResolved, onRejected) {
  2. // 规范:如果 onFulfilled 不是函数,其必须被忽略
  3. // 这里,若onResolved, onRejected 不是函数,则用一个过渡性的函数代替
  4. onResolved = typeof onResolved === 'function'? onResolved:function(value) {
  5. return value; // 将value原封不动地传给下一个then,相当于跳过(忽略)本轮then的onResolved
  6. }
  7. onRejected = typeof onRejected === 'function'? onRejected:function(err) {
  8. throw err; // 同上,相当于跳过(忽略)本轮then的onRejected
  9. }
  10. var _this = this
  11. var promise2 // then始终返回一个Promise实例,用于链式调用。
  12. if (_this._status === "resolved") {
  13. promise2 = new Promise(function (resolve, reject){
  14. setTimeout(function() { // 确保onResolved 和 onRejected 方法异步执行。下面的setTimeout同理
  15. try {
  16. var x = onResolved(_this._value)
  17. resolvePromise(promise2, x, resolve, reject) // resolvePromise内执行promise2的resolve和reject
  18. } catch (e) {
  19. reject(e)
  20. }
  21. }, 0);
  22. })
  23. }
  24. if (_this._status === "rejected") {
  25. promise2 = new Promise(function (resolve, reject){
  26. setTimeout(function() {
  27. try {
  28. var x = onRejected(_this._reason)
  29. resolvePromise(promise2, x, resolve, reject)
  30. } catch (e) {
  31. reject(e)
  32. }
  33. }, 0);
  34. })
  35. }
  36. if (_this._status === "pending") {
  37. // resolve或reject前调用then的话,将回调推入队列
  38. promise2 = new Promise(function (resolve, reject) {
  39. _this._onResolveds.push(function () {
  40. setTimeout(function() {
  41. try {
  42. var x = onResolved(_this._value)
  43. resolvePromise(promise2, x, resolve, reject)
  44. } catch (e) {
  45. reject(e)
  46. }
  47. }, 0);
  48. });
  49. _this._onRejecteds.push(function () {
  50. setTimeout(function() {
  51. try {
  52. var x = onRejected(_this._reason)
  53. resolvePromise(promise2, x, resolve, reject)
  54. } catch (e) {
  55. reject(e)
  56. }
  57. }, 0);
  58. });
  59. })
  60. }
  61. return promise2
  62. };

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

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

测试:

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

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

  1. Promise.deferred = Promise.defer = function () {
  2. let deferred = {};
  3. deferred.promise = new Promise(function (resolve, reject) {
  4. deferred.resolve = resolve;
  5. deferred.reject = reject;
  6. });
  7. return deferred
  8. };
  9. module.exports = Promise

测试完成后可删除

使用 UMD 规范封装:

  1. (function (global, factory) {
  2. if (typeof define === 'function' && define.amd) {
  3. // AMD
  4. define(factory)
  5. } else if (typeof exports === 'object' && typeof module !== 'undefined') {
  6. // CommonJS (如node)
  7. module.exports = factory()
  8. } else {
  9. // 浏览器全局变量
  10. global.promisePolyfill = factory()
  11. }
  12. })(this, function () {
  13. 'use strict';
  14. /*
  15. 定义Promise的代码
  16. */
  17. function promisePolyfill () {
  18. var global = null
  19. try {
  20. global = Function('return this')();
  21. } catch (e) {
  22. throw new Error('全局对象不可用');
  23. }
  24. global.Promise = Promise
  25. }
  26. return promisePolyfill
  27. })

使用

  1. 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. 使用eclipse创建maven项目出现的一个问题

    错误信息 This error occurs when you employ a plugin that Maven could not download. Possible causes for t ...

  2. Django基本目录详解

    1.app是自己建立的一个存放app的文件夹,因为项目大了之后会存在很多app(pycharm生成app方法 Tools-Run manage.py Task-输入startapp app名称) 2. ...

  3. IMX6移植htop

    top命令查看CPU利用率并不是很方便,因此打算移植htop到imx6上,主要包括以下几个步骤: - 资源下载 htop 下载http://hisham.hm/htop/releases/1.0.1/ ...

  4. CDH问题集

    1.在CM中添加主机报JDK错误 手动在机器上安装oracle-jdk1.7+update64.然后在CM中选择不安装oracle-jdk即可. 2.HostMoinitor无法与server联系 查 ...

  5. js经典试题之w3规范系列

    js经典试题之w3规范系列 1:w3c 制定的 javascript 标准事件模型的正确的顺序? 答案:事件捕获->事件处理->事件冒泡 解析:先事件捕获从windows > doc ...

  6. Beta版本软件使用说明

    北京航空航天大学计算机学院 远航1617 小组 产品版本: Beta版本 产品名称:Crawling   is going on 文档作者:杨帆 文档日期:2013/12/24 1.   引言 1.1 ...

  7. 创建、编译、执行 java程序

    java源文件(.java)——Java字节码文件(.class)——在java虚拟机上执行 其他语言很多是编译后执行,所以无法跨平台

  8. lintcode-42-最大子数组 II

    42-最大子数组 II 给定一个整数数组,找出两个 不重叠 子数组使得它们的和最大. 每个子数组的数字在数组中的位置应该是连续的. 返回最大的和. 注意事项 子数组最少包含一个数 样例 给出数组 [1 ...

  9. Winform 数据绑定

    1.DataGridView数据绑定 namespace WindowsFormsApplication1 { public partial class Form1 : Form { private ...

  10. Qt Meta Object system 学习

    原文地址:http://blog.csdn.net/ilvu999/article/details/8049908 使用 meta object system 继承自 QOject 类定义中添加 Q_ ...