├── .eslintrc
├── .github
└── workflows
│ ├── node-aught.yml
│ ├── node-native.yml
│ ├── node-pretest.yml
│ ├── node-promise-shimmed.yml
│ ├── node-tens.yml
│ ├── rebase.yml
│ └── require-allow-edits.yml
├── .gitignore
├── .npmignore
├── .npmrc
├── .nycrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── auto.js
├── implementation.js
├── index.js
├── package.json
├── polyfill.js
├── requirePromise.js
├── shim.js
└── test
├── builtin.js
├── implementation.js
├── index.js
├── native.js
├── promise-shimmed.js
├── shimmed.js
└── tests.js
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 |
4 | "extends": "@ljharb",
5 |
6 | "rules": {
7 | "new-cap": [2, {
8 | "capIsNewExceptions": [
9 | "IsCallable",
10 | "PromiseResolve",
11 | "RequireObjectCoercible",
12 | "SpeciesConstructor",
13 | "Type"
14 | ],
15 | }],
16 | "no-magic-numbers": 0,
17 | },
18 |
19 | "overrides": [
20 | {
21 | "files": "test/**/*",
22 | "rules": {
23 | "max-lines-per-function": 0,
24 | "max-params": 0,
25 | "no-invalid-this": 1,
26 | "prefer-promise-reject-errors": 0,
27 | },
28 | },
29 | ],
30 | }
31 |
--------------------------------------------------------------------------------
/.github/workflows/node-aught.yml:
--------------------------------------------------------------------------------
1 | name: 'Tests: node.js < 10'
2 |
3 | on: [pull_request, push]
4 |
5 | jobs:
6 | tests:
7 | uses: ljharb/actions/.github/workflows/node.yml@main
8 | with:
9 | range: '>= 0.11 < 10'
10 | type: minors
11 | command: npm run tests-only
12 |
13 | node:
14 | name: 'node < 10'
15 | needs: [tests]
16 | runs-on: ubuntu-latest
17 | steps:
18 | - run: 'echo tests completed'
19 |
--------------------------------------------------------------------------------
/.github/workflows/node-native.yml:
--------------------------------------------------------------------------------
1 | name: 'Tests: node.js: native'
2 |
3 | on: [pull_request, push]
4 |
5 | jobs:
6 | tests:
7 | uses: ljharb/actions/.github/workflows/node.yml@main
8 | with:
9 | range: '>= 10'
10 | type: majors
11 | continue-on-error: true
12 | command: npm run test:native
13 |
14 | node:
15 | name: 'node, native tests'
16 | needs: [tests]
17 | runs-on: ubuntu-latest
18 | steps:
19 | - run: 'echo tests completed'
20 |
--------------------------------------------------------------------------------
/.github/workflows/node-pretest.yml:
--------------------------------------------------------------------------------
1 | name: 'Tests: pretest/posttest'
2 |
3 | on: [pull_request, push]
4 |
5 | jobs:
6 | tests:
7 | uses: ljharb/actions/.github/workflows/pretest.yml@main
8 |
--------------------------------------------------------------------------------
/.github/workflows/node-promise-shimmed.yml:
--------------------------------------------------------------------------------
1 | name: 'Tests: node.js: shimmed Promise'
2 |
3 | on: [pull_request, push]
4 |
5 | jobs:
6 | majors:
7 | name: 'majors, promise-shimmed tests'
8 | continue-on-error: true
9 | runs-on: ubuntu-latest
10 |
11 | strategy:
12 | fail-fast: false
13 | matrix:
14 | node-version:
15 | - '0.12'
16 | - '0.11'
17 | - '0.10'
18 | - '0.8'
19 |
20 | steps:
21 | - uses: actions/checkout@v4
22 | - uses: ljharb/actions/node/install@main
23 | name: 'nvm install ${{ matrix.node-version }} && npm install'
24 | with:
25 | node-version: ${{ matrix.node-version }}
26 | - run: npm run test:promise-shimmed
27 |
28 | node:
29 | name: 'node, promise-shimmed tests'
30 | needs: [majors]
31 | runs-on: ubuntu-latest
32 | steps:
33 | - run: 'echo tests completed'
34 |
--------------------------------------------------------------------------------
/.github/workflows/node-tens.yml:
--------------------------------------------------------------------------------
1 | name: 'Tests: node.js >= 10'
2 |
3 | on: [pull_request, push]
4 |
5 | jobs:
6 | tests:
7 | uses: ljharb/actions/.github/workflows/node.yml@main
8 | with:
9 | range: '>= 10'
10 | type: minors
11 | command: npm run tests-only
12 |
13 | node:
14 | name: 'node >= 10'
15 | needs: [tests]
16 | runs-on: ubuntu-latest
17 | steps:
18 | - run: 'echo tests completed'
19 |
--------------------------------------------------------------------------------
/.github/workflows/rebase.yml:
--------------------------------------------------------------------------------
1 | name: Automatic Rebase
2 |
3 | on: [pull_request_target]
4 |
5 | jobs:
6 | _:
7 | uses: ljharb/actions/.github/workflows/rebase.yml@main
8 | secrets:
9 | token: ${{ secrets.GITHUB_TOKEN }}
10 |
--------------------------------------------------------------------------------
/.github/workflows/require-allow-edits.yml:
--------------------------------------------------------------------------------
1 | name: Require “Allow Edits”
2 |
3 | on: [pull_request_target]
4 |
5 | jobs:
6 | _:
7 | name: "Require “Allow Edits”"
8 |
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - uses: ljharb/require-allow-edits@main
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | # Only apps should have lockfiles
40 | package-lock.json
41 | yarn.lock
42 | npm-shrinkwrap.json
43 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | # Only apps should have lockfiles
40 | package-lock.json
41 | yarn.lock
42 | npm-shrinkwrap.json
43 |
44 | .github/workflows
45 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 | allow-same-version=true
3 | message=v%s
4 |
--------------------------------------------------------------------------------
/.nycrc:
--------------------------------------------------------------------------------
1 | {
2 | "all": true,
3 | "check-coverage": false,
4 | "reporter": ["text-summary", "text", "html", "json"],
5 | "lines": 86,
6 | "statements": 85.93,
7 | "functions": 82.43,
8 | "branches": 76.06,
9 | "exclude": [
10 | "coverage",
11 | "test"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## [v3.1.8](https://github.com/es-shims/Promise.prototype.finally/compare/v3.1.7...v3.1.8) - 2024-02-04
9 |
10 | ### Commits
11 |
12 | - [Refactor] use `es-errors` where possible, so things that only need those do not need `get-intrinsic` [`9f15e2a`](https://github.com/es-shims/Promise.prototype.finally/commit/9f15e2a679b1f3e16f5bf005bf62569896a68a65)
13 | - [Deps] update `call-bind`, `es-abstract`, `get-intrinsic` [`4dbd141`](https://github.com/es-shims/Promise.prototype.finally/commit/4dbd141a4eebbba1cc3c7b54a6059457bf9c06b6)
14 | - [Dev Deps] update `aud`, `tape` [`3661fdb`](https://github.com/es-shims/Promise.prototype.finally/commit/3661fdbd28000474d20a2135f8fcc7317b319c77)
15 |
16 | ## [v3.1.7](https://github.com/es-shims/Promise.prototype.finally/compare/v3.1.6...v3.1.7) - 2023-09-13
17 |
18 | ### Commits
19 |
20 | - [Deps] update `define-properties`, `set-function-name` [`01d3f17`](https://github.com/es-shims/Promise.prototype.finally/commit/01d3f17514abb9da154890c28e317bd3b3ccddfd)
21 |
22 | ## [v3.1.6](https://github.com/es-shims/Promise.prototype.finally/compare/v3.1.5...v3.1.6) - 2023-09-13
23 |
24 | ### Commits
25 |
26 | - [Refactor] use `set-function-name` [`903d207`](https://github.com/es-shims/Promise.prototype.finally/commit/903d2071f0fc8391ce69fa249915067d57a59332)
27 | - [actions] update checkout action [`594ef8e`](https://github.com/es-shims/Promise.prototype.finally/commit/594ef8ef4cb71189eb867cedeb6b201c7b2e27c2)
28 |
29 | ## [v3.1.5](https://github.com/es-shims/Promise.prototype.finally/compare/v3.1.4...v3.1.5) - 2023-08-30
30 |
31 | ### Commits
32 |
33 | - [Deps] update `define-properties`, `es-abstract` [`2ff6ac3`](https://github.com/es-shims/Promise.prototype.finally/commit/2ff6ac356367e89eb555c50e3522e815d9d1bbbf)
34 | - [Dev Deps] update `@es-shims/api`, `@ljharb/eslint-config`, `aud`, `es6-shim`, `tape` [`4cacca4`](https://github.com/es-shims/Promise.prototype.finally/commit/4cacca47298df952f4547c5d7cdad5226c6266b8)
35 |
36 | ## [v3.1.4](https://github.com/es-shims/Promise.prototype.finally/compare/v3.1.3...v3.1.4) - 2022-11-07
37 |
38 | ### Commits
39 |
40 | - [actions] reuse common workflows [`1f2f581`](https://github.com/es-shims/Promise.prototype.finally/commit/1f2f581ffc86fcc76c91ad9b4e36466c23e370a0)
41 | - [meta] add `auto-changelog` [`382073c`](https://github.com/es-shims/Promise.prototype.finally/commit/382073ccb71bc7f41977c112d316da1a33e1148d)
42 | - [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `safe-publish-latest`, `tape` [`82cee30`](https://github.com/es-shims/Promise.prototype.finally/commit/82cee3007dc2641d22542e3d105e5fb95caee61b)
43 | - [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `@es-shims/api`, `aud`, `tape` [`7a16cda`](https://github.com/es-shims/Promise.prototype.finally/commit/7a16cdadad7fc32548b9cd3aff3ba160968d85ed)
44 | - [actions] update rebase action to use reusable workflow [`a3cefcf`](https://github.com/es-shims/Promise.prototype.finally/commit/a3cefcf3d2774834477f4263eedcd5abb089b651)
45 | - [actions] update codecov uploader [`63f0668`](https://github.com/es-shims/Promise.prototype.finally/commit/63f06684ac969bc4a78afa8a96a61d1034055885)
46 | - [Deps] update `define-properties`, `es-abstract` [`efeba8d`](https://github.com/es-shims/Promise.prototype.finally/commit/efeba8d7ce3ec0a522b639c492d2c27e0f2991e0)
47 |
48 |
49 |
50 | 3.1.3 / 2021-10-04
51 | =================
52 | * [Refactor] update `es-abstract`; use `call-bind` instead of `function-bind`
53 | * [Deps] update `es-abstract`
54 | * [readme] add github actions/codecov badges
55 | * [meta] remove unneeded token; update checkout action
56 | * [actions] use `node/install` instead of `node/run`; use `codecov` action
57 | * [actions] add Require Allow Edits workflow
58 | * [actions] switch Automatic Rebase workflow to `pull_request_target` event
59 | * [Tests] increase coverage
60 | * [Tests] migrate tests to Github Actions (#29)
61 | * [Tests] run `nyc` on all tests; use `tape` runner; add implementation tests; mark failing impl tests as TODO
62 | * [Tests] skip "observable calls" tests in node 6-9
63 | * [Tests] add passing tests from https://github.com/tc39/test262/pull/2752
64 | * [Tests] refactor Subclass tests to capture receiver
65 | * [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `@es-shims/api`, `aud`, `es6-shim`, `tape`
66 |
67 | 3.1.2 / 2019-12-11
68 | =================
69 | * [Refactor] use split-up `es-abstract`
70 | * [Deps] update `es-abstract`
71 | * [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `safe-publish-latest`
72 | * [Tests] up to `node` `v12.12`
73 | * [Tests] use shared travis-ci configs
74 | * [meta] add `funding` field
75 | * [actions] add automatic rebasing / merge commit blocking
76 |
77 | 3.1.1 / 2019-08-25
78 | =================
79 | * [Fix] `finally` receiver must only be an Object, not a Promise
80 | * [Deps] update `define-properties`, `es-abstract`
81 | * [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `@es-shims/api`, `covert`, `es6-shim`, `safe-publish-latest`, `tape`
82 | * [Tests] up to `node` `v12.9`, `v11.15`, `v10.16`, `v9.11`, `v8.16`, `v6.17`, `v4.9`
83 | * [Tests] add test for non-Promise receiver
84 | * [Tests] use `npx aud` instead of `nsp` or `npm audit` with hoops
85 |
86 | 3.1.0 / 2017-10-26
87 | =================
88 | * [New] Add auto shim file, allowing clean 'import' (#12)
89 | * [Refactor] only call `Promise#then` for a brand check once, instead of twice.
90 | * [Deps] update `es-abstract`
91 | * [Dev Deps] update `eslint`, `nsp`
92 |
93 | 3.0.1 / 2017-09-09
94 | =================
95 | * [Fix] ensure that the “then” brand check doesn’t cause an unhandled rejection warning (#10)
96 | * [Deps] update `es-abstract`, `function-bind`
97 | * [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `nsp`, `tape`, `@es-shims/api`
98 | * [Tests] up to `node` `v8.4`; use `nvm install-latest-npm` so new `npm` doesn’t break old `node`; add 0.8
99 | * [Tests] restore ES5 tests
100 | * [Tests] refactor to allow for unshimmed tests
101 |
102 | 3.0.0 / 2017-07-25
103 | =================
104 | * [Breaking] update implementation to follow the new spec (#9)
105 | * [Deps] update `es-abstract`
106 | * [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `es6-shim`, `nsp`, `safe-publish-latest`, `tape`
107 | * [Tests] up to `node` `v8.1`, `v7.10`, `v6.11`, `v4.8`; improve matrix
108 | * [Tests] fix 0.10; remove 0.8
109 |
110 | 2.0.1 / 2016-09-27
111 | =================
112 | * [Fix] functions in IE 9-11 don’t have a `name` property (#3)
113 |
114 | 2.0.0 / 2016-08-21
115 | =================
116 | * Re-release.
117 |
118 | [1.0.1](https://github.com/matthew-andrews/Promise.prototype.finally/releases/tag/v1.0.1) / 2015-02-09
119 | =================
120 | * Always replace function for predictability (https://github.com/matthew-andrews/Promise.prototype.finally/issues/3)
121 | * Wrap polyfill so that if it's used direct it doesn't leak
122 |
123 | [1.0.0](https://github.com/matthew-andrews/Promise.prototype.finally/releases/tag/v1.0.0) / 2014-10-11
124 | =================
125 | * Initial release.
126 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Jordan Harband
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # promise.prototype.finally [![Version Badge][npm-version-svg]][package-url]
2 |
3 | [![github actions][actions-image]][actions-url]
4 | [![coverage][codecov-image]][codecov-url]
5 | [![dependency status][deps-svg]][deps-url]
6 | [![dev dependency status][dev-deps-svg]][dev-deps-url]
7 | [![License][license-image]][license-url]
8 | [![Downloads][downloads-image]][downloads-url]
9 |
10 | [![npm badge][npm-badge-png]][package-url]
11 |
12 | ES Proposal spec-compliant shim for Promise.prototype.finally. Invoke its "shim" method to shim `Promise.prototype.finally` if it is unavailable or noncompliant. **Note**: a global `Promise` must already exist: the [es6-shim](https://github.com/es-shims/es6-shim) is recommended.
13 |
14 | This package implements the [es-shim API](https://github.com/es-shims/api) interface. It works in an ES3-supported environment that has `Promise` available globally, and complies with the [proposed spec](https://github.com/tc39/proposal-promise-finally).
15 |
16 | Most common usage:
17 | ```js
18 | var assert = require('assert');
19 | var promiseFinally = require('promise.prototype.finally');
20 |
21 | var resolved = Promise.resolve(42);
22 | var rejected = Promise.reject(-1);
23 |
24 | promiseFinally(resolved, function () {
25 | assert.equal(arguments.length, 0);
26 |
27 | return Promise.resolve(true);
28 | }).then(function (x) {
29 | assert.equal(x, 42);
30 | });
31 |
32 | promiseFinally(rejected, function () {
33 | assert.equal(arguments.length, 0);
34 | }).catch(function (e) {
35 | assert.equal(e, -1);
36 | });
37 |
38 | promiseFinally(rejected, function () {
39 | assert.equal(arguments.length, 0);
40 |
41 | throw false;
42 | }).catch(function (e) {
43 | assert.equal(e, false);
44 | });
45 |
46 | promiseFinally.shim(); // will be a no-op if not needed
47 |
48 | resolved.finally(function () {
49 | assert.equal(arguments.length, 0);
50 |
51 | return Promise.resolve(true);
52 | }).then(function (x) {
53 | assert.equal(x, 42);
54 | });
55 |
56 | rejected.finally(function () {
57 | assert.equal(arguments.length, 0);
58 | }).catch(function (e) {
59 | assert.equal(e, -1);
60 | });
61 |
62 | rejected.finally(function () {
63 | assert.equal(arguments.length, 0);
64 |
65 | throw false;
66 | }).catch(function (e) {
67 | assert.equal(e, false);
68 | });
69 | ```
70 |
71 | ## Tests
72 | Simply clone the repo, `npm install`, and run `npm test`
73 |
74 | ## Thanks
75 | Huge thanks go out to [@matthew-andrews](https://github.com/matthew-andrews), who provided the npm package name for v2 of this module. v1 is both in [the original repo][v1-repo-url] and preserved in [a branch][v1-branch-url]
76 |
77 | [package-url]: https://npmjs.com/package/promise.prototype.finally
78 | [npm-version-svg]: http://versionbadg.es/es-shims/Promise.prototype.finally.svg
79 | [deps-svg]: https://david-dm.org/es-shims/Promise.prototype.finally.svg
80 | [deps-url]: https://david-dm.org/es-shims/Promise.prototype.finally
81 | [dev-deps-svg]: https://david-dm.org/es-shims/Promise.prototype.finally/dev-status.svg
82 | [dev-deps-url]: https://david-dm.org/es-shims/Promise.prototype.finally#info=devDependencies
83 | [testling-svg]: https://ci.testling.com/es-shims/Promise.prototype.finally.png
84 | [testling-url]: https://ci.testling.com/es-shims/Promise.prototype.finally
85 | [npm-badge-png]: https://nodei.co/npm/promise.prototype.finally.png?downloads=true&stars=true
86 | [license-image]: http://img.shields.io/npm/l/promise.prototype.finally.svg
87 | [license-url]: LICENSE
88 | [downloads-image]: http://img.shields.io/npm/dm/promise.prototype.finally.svg
89 | [downloads-url]: http://npm-stat.com/charts.html?package=promise.prototype.finally
90 | [v1-repo-url]: https://github.com/matthew-andrews/Promise.prototype.finally
91 | [v1-branch-url]: https://github.com/es-shims/Promise.prototype.finally/tree/v1
92 | [codecov-image]: https://codecov.io/gh/es-shims/Promise.prototype.finally/branch/main/graphs/badge.svg
93 | [codecov-url]: https://app.codecov.io/gh/es-shims/Promise.prototype.finally/
94 | [actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/es-shims/Promise.prototype.finally
95 | [actions-url]: https://github.com/es-shims/Promise.prototype.finally/actions
96 |
--------------------------------------------------------------------------------
/auto.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('./shim')();
4 |
--------------------------------------------------------------------------------
/implementation.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var requirePromise = require('./requirePromise');
4 |
5 | requirePromise();
6 |
7 | var $TypeError = require('es-errors/type');
8 |
9 | var PromiseResolve = require('es-abstract/2023/PromiseResolve');
10 | var IsCallable = require('es-abstract/2023/IsCallable');
11 | var SpeciesConstructor = require('es-abstract/2023/SpeciesConstructor');
12 | var Type = require('es-abstract/2023/Type');
13 |
14 | var setFunctionName = require('set-function-name');
15 |
16 | var OriginalPromise = Promise;
17 |
18 | var createThenFinally = function CreateThenFinally(C, onFinally) {
19 | return function (value) {
20 | var result = onFinally();
21 | var promise = PromiseResolve(C, result);
22 | var valueThunk = function () {
23 | return value;
24 | };
25 | return promise.then(valueThunk);
26 | };
27 | };
28 |
29 | var createCatchFinally = function CreateCatchFinally(C, onFinally) {
30 | return function (reason) {
31 | var result = onFinally();
32 | var promise = PromiseResolve(C, result);
33 | var thrower = function () {
34 | throw reason;
35 | };
36 | return promise.then(thrower);
37 | };
38 | };
39 |
40 | /* eslint no-invalid-this: 0 */
41 |
42 | module.exports = setFunctionName(function finally_(onFinally) {
43 | var promise = this;
44 |
45 | if (Type(promise) !== 'Object') {
46 | throw new $TypeError('receiver is not an Object');
47 | }
48 |
49 | var C = SpeciesConstructor(promise, OriginalPromise); // may throw
50 |
51 | var thenFinally = onFinally;
52 | var catchFinally = onFinally;
53 | if (IsCallable(onFinally)) {
54 | thenFinally = createThenFinally(C, onFinally);
55 | catchFinally = createCatchFinally(C, onFinally);
56 | }
57 |
58 | return promise.then(thenFinally, catchFinally);
59 | }, 'finally', true);
60 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var callBind = require('call-bind');
4 | var define = require('define-properties');
5 |
6 | var implementation = require('./implementation');
7 | var getPolyfill = require('./polyfill');
8 | var shim = require('./shim');
9 |
10 | var bound = callBind(getPolyfill());
11 |
12 | define(bound, {
13 | getPolyfill: getPolyfill,
14 | implementation: implementation,
15 | shim: shim
16 | });
17 |
18 | module.exports = bound;
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "promise.prototype.finally",
3 | "version": "3.1.8",
4 | "author": "Jordan Harband ",
5 | "funding": {
6 | "url": "https://github.com/sponsors/ljharb"
7 | },
8 | "contributors": [
9 | {
10 | "name": "Matt Andrews",
11 | "email": "matt@mattandre.ws"
12 | },
13 | {
14 | "name": "Jordan Harband",
15 | "email": "ljharb@gmail.com",
16 | "url": "http://ljharb.codes"
17 | }
18 | ],
19 | "description": "ES Proposal spec-compliant shim for Promise.prototype.finally",
20 | "license": "MIT",
21 | "main": "index.js",
22 | "scripts": {
23 | "prepublishOnly": "safe-publish-latest",
24 | "prepublish": "not-in-publish || npm run prepublishOnly",
25 | "pretest": "npm run --silent lint",
26 | "test": "npm run --silent tests-only",
27 | "posttest": "npx aud --production",
28 | "tests-only": "nyc tape test/{implementation,index,shimmed}.js",
29 | "test:promise-shimmed": "nyc node test/promise-shimmed",
30 | "test:native": "nyc node test/native",
31 | "lint": "eslint --ext=.js,.mjs .",
32 | "postlint": "es-shim-api --bound",
33 | "version": "auto-changelog && git add CHANGELOG.md",
34 | "postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
35 | },
36 | "repository": {
37 | "type": "git",
38 | "url": "git://github.com/es-shims/Promise.prototype.finally.git"
39 | },
40 | "keywords": [
41 | "Promise",
42 | "promises",
43 | "finally",
44 | "promise.prototype.finally",
45 | "ES7",
46 | "ES8",
47 | "ES2017",
48 | "shim",
49 | "polyfill",
50 | "es-shim API"
51 | ],
52 | "dependencies": {
53 | "call-bind": "^1.0.5",
54 | "define-properties": "^1.2.1",
55 | "es-abstract": "^1.22.3",
56 | "es-errors": "^1.0.0",
57 | "set-function-name": "^2.0.1"
58 | },
59 | "devDependencies": {
60 | "@es-shims/api": "^2.4.2",
61 | "@ljharb/eslint-config": "^21.1.0",
62 | "aud": "^2.0.4",
63 | "auto-changelog": "^2.4.0",
64 | "es6-shim": "^0.35.8",
65 | "eslint": "=8.8.0",
66 | "in-publish": "^2.0.1",
67 | "nyc": "^10.3.2",
68 | "safe-publish-latest": "^2.0.0",
69 | "tape": "^5.7.4"
70 | },
71 | "testling": {
72 | "files": "test/index.js",
73 | "browsers": [
74 | "iexplore/9.0..latest",
75 | "firefox/4.0..6.0",
76 | "firefox/15.0..latest",
77 | "firefox/nightly",
78 | "chrome/4.0..10.0",
79 | "chrome/20.0..latest",
80 | "chrome/canary",
81 | "opera/11.6..latest",
82 | "opera/next",
83 | "safari/5.0..latest",
84 | "ipad/6.0..latest",
85 | "iphone/6.0..latest",
86 | "android-browser/4.2"
87 | ]
88 | },
89 | "auto-changelog": {
90 | "output": "CHANGELOG.md",
91 | "template": "keepachangelog",
92 | "unreleased": false,
93 | "commitLimit": false,
94 | "backfillLimit": false,
95 | "hideCredit": true,
96 | "startingVersion": "3.1.4"
97 | },
98 | "engines": {
99 | "node": ">= 0.4"
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/polyfill.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var requirePromise = require('./requirePromise');
4 |
5 | var implementation = require('./implementation');
6 |
7 | module.exports = function getPolyfill() {
8 | requirePromise();
9 | return typeof Promise.prototype['finally'] === 'function' ? Promise.prototype['finally'] : implementation;
10 | };
11 |
--------------------------------------------------------------------------------
/requirePromise.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function requirePromise() {
4 | if (typeof Promise !== 'function') {
5 | throw new TypeError('`Promise.prototype.finally` requires a global `Promise` be available.');
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/shim.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var requirePromise = require('./requirePromise');
4 |
5 | var getPolyfill = require('./polyfill');
6 | var define = require('define-properties');
7 |
8 | module.exports = function shimPromiseFinally() {
9 | requirePromise();
10 |
11 | var polyfill = getPolyfill();
12 | define(Promise.prototype, { 'finally': polyfill }, {
13 | 'finally': function testFinally() {
14 | return Promise.prototype['finally'] !== polyfill;
15 | }
16 | });
17 | return polyfill;
18 | };
19 |
--------------------------------------------------------------------------------
/test/builtin.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var defineProperties = require('define-properties');
4 | var callBind = require('call-bind');
5 | var isEnumerable = Object.prototype.propertyIsEnumerable;
6 | var functionsHaveNames = function f() {}.name === 'f';
7 | var fnNamesConfigurable = functionsHaveNames && Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(function f() {}, 'name').configurable;
8 |
9 | var runTests = require('./tests');
10 |
11 | module.exports = function (t) {
12 | t.equal(Promise.prototype['finally'].length, 1, 'Promise.prototype.finally has a length of 1');
13 | t.test('Function name', { skip: !fnNamesConfigurable }, function (st) {
14 | st.equal(Promise.prototype['finally'].name, 'finally', 'Promise.prototype.finally has name "finally"');
15 | st.end();
16 | });
17 |
18 | t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) {
19 | et.equal(false, isEnumerable.call(Promise.prototype, 'finally'), 'Promise.prototype.finally is not enumerable');
20 | et.end();
21 | });
22 |
23 | var supportsStrictMode = (function () { return typeof this === 'undefined'; }());
24 |
25 | t.test('bad object value', { skip: !supportsStrictMode }, function (st) {
26 | st['throws'](function () { return Promise.prototype['finally'].call(undefined); }, TypeError, 'undefined is not an object');
27 | st['throws'](function () { return Promise.prototype['finally'].call(null); }, TypeError, 'null is not an object');
28 | st.end();
29 | });
30 |
31 | runTests(callBind(Promise.prototype['finally']), t);
32 | };
33 |
--------------------------------------------------------------------------------
/test/implementation.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var test = require('tape');
4 | var callBind = require('call-bind');
5 |
6 | var promiseFinally = require('../implementation');
7 | var runTests = require('./tests');
8 |
9 | var bound = callBind(promiseFinally);
10 |
11 | test('as a function', function (t) {
12 | t.test('bad Promise/this value', function (st) {
13 | // eslint-disable-next-line no-useless-call
14 | st['throws'](function () { promiseFinally.call(undefined); }, TypeError, 'undefined is not an object');
15 |
16 | // eslint-disable-next-line no-useless-call
17 | st['throws'](function () { promiseFinally.call(null); }, TypeError, 'null is not an object');
18 | st.end();
19 | });
20 |
21 | runTests(bound, t);
22 |
23 | t.end();
24 | });
25 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var promiseFinally = require('../');
4 | var test = require('tape');
5 | var runTests = require('./tests');
6 |
7 | test('as a function', function (t) {
8 | t.test('bad Promise/this value', function (st) {
9 | st['throws'](function () { promiseFinally(undefined); }, TypeError, 'undefined is not an object');
10 | st['throws'](function () { promiseFinally(null); }, TypeError, 'null is not an object');
11 | st.end();
12 | });
13 |
14 | runTests(promiseFinally, t);
15 |
16 | t.end();
17 | });
18 |
--------------------------------------------------------------------------------
/test/native.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var test = require('tape');
4 |
5 | var runTests = require('./builtin');
6 |
7 | test('shimmed', function (t) {
8 | runTests(t);
9 |
10 | t.end();
11 | });
12 |
--------------------------------------------------------------------------------
/test/promise-shimmed.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('es6-shim');
4 |
5 | require('./');
6 |
7 | require('./shimmed');
8 |
--------------------------------------------------------------------------------
/test/shimmed.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('../auto');
4 |
5 | var test = require('tape');
6 |
7 | var runTests = require('./builtin');
8 |
9 | test('shimmed', function (t) {
10 | runTests(t);
11 |
12 | t.end();
13 | });
14 |
--------------------------------------------------------------------------------
/test/tests.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var assertArray = function (t, value, length, assertType) {
4 | t.ok(Array.isArray(value), 'value is an array');
5 | t.equal(value.length, length, 'length is ' + length);
6 | if (typeof assertType === 'function') {
7 | for (var i = 0; i < value.length; i += 1) {
8 | assertType(value[i]);
9 | }
10 | }
11 | };
12 |
13 | module.exports = function (promiseFinally, t) {
14 | if (typeof Promise !== 'function') {
15 | return t.skip('No global Promise detected');
16 | }
17 |
18 | t.test('non-Promises', function (st) {
19 | var sentinel = {};
20 | var onFulfill = function () {};
21 | var onReject = function () {};
22 | var results = promiseFinally(
23 | { then: function () { return [sentinel].concat(Array.prototype.slice.call(arguments)); } },
24 | onFulfill,
25 | onReject
26 | );
27 | st.equal(results[0], sentinel, 'a receiver with a custom `then` has its return value returned immediately');
28 | st.equal(typeof results[1], 'function', 'a receiver with a custom `then` gets the right arguments');
29 | st.equal(typeof results[2], 'function', 'a receiver with a custom `then` gets the right arguments');
30 | st.end();
31 | });
32 |
33 | t.test('onFinally arguments', function (st) {
34 | st.plan(4);
35 |
36 | promiseFinally(Promise.resolve(42), function () {
37 | st.equal(arguments.length, 0, 'resolved promise passes no arguments to onFinally');
38 | }).then(st.pass, st.fail);
39 |
40 | promiseFinally(Promise.reject(NaN), function () {
41 | st.equal(arguments.length, 0, 'rejected promise passes no arguments to onFinally');
42 | }).then(st.fail, st.pass);
43 | });
44 |
45 | t.test('onFinally fulfillment', function (st) {
46 | st.plan(6);
47 |
48 | promiseFinally(Promise.resolve(42), function () { return Promise.resolve(Infinity); }).then(function (x) {
49 | st.equal(x, 42, 'resolved promise onFinally resolution does not affect promise resolution value');
50 | })['catch'](st.fail);
51 |
52 | promiseFinally(Promise.resolve(42), function () { return Promise.reject(-Infinity); })['catch'](function (x) {
53 | st.equal(x, -Infinity, 'resolved promise onFinally returning a rejected Promise rejects with the new rejection value');
54 | })['catch'](st.fail);
55 |
56 | promiseFinally(Promise.resolve(42), function () { throw Function; })['catch'](function (e) {
57 | st.equal(e, Function, 'resolved promise onFinally throwing rejects with the thrown rejection value');
58 | })['catch'](st.fail);
59 |
60 | promiseFinally(Promise.reject(42), function () { return Promise.resolve(Infinity); })['catch'](function (e) {
61 | st.equal(e, 42, 'rejected promise onFinally resolution does not affect promise rejection value');
62 | })['catch'](st.fail);
63 |
64 | promiseFinally(Promise.reject(42), function () { return Promise.reject(-Infinity); })['catch'](function (x) {
65 | st.equal(x, -Infinity, 'rejected promise onFinally returning a rejected Promise rejects with the new rejection value');
66 | })['catch'](st.fail);
67 |
68 | promiseFinally(Promise.reject(42), function () { throw Function; })['catch'](function (e) {
69 | st.equal(e, Function, 'rejected promise onFinally throwing rejects with the thrown rejection value');
70 | })['catch'](st.fail);
71 | });
72 |
73 | var Subclass = (function () {
74 | try {
75 | // eslint-disable-next-line no-new-func
76 | return Function('class Subclass extends Promise { constructor(...args) { super(...args); this.thenArgs = []; } then(...args) { Subclass.thenArgs.push({ promise: this, args: args }); this.thenArgs.push({ promise: this, args: args }); return super.then(...args); } } Subclass.thenArgs = []; return Subclass;')();
77 | } catch (e) { /**/ }
78 |
79 | return false;
80 | }());
81 | t.test('inheritance', { skip: !Subclass }, function (st) {
82 | st.test('preserves correct subclass when chained', function (s2t) {
83 | var promise = promiseFinally(Subclass.resolve());
84 | s2t.ok(promise instanceof Subclass, 'promise is instanceof Subclass');
85 | s2t.equal(promise.constructor, Subclass, 'promise.constructor is Subclass');
86 |
87 | s2t.end();
88 | });
89 |
90 | st.test('preserves correct subclass when rejected', function (s2t) {
91 | var promise = promiseFinally(Subclass.resolve(), function () {
92 | throw new Error('OMG');
93 | });
94 | s2t.ok(promise instanceof Subclass, 'promise is instanceof Subclass');
95 | s2t.equal(promise.constructor, Subclass, 'promise.constructor is Subclass');
96 |
97 | promise['catch'](function () {}); // avoid unhandled rejection warning
98 |
99 | s2t.end();
100 | });
101 |
102 | st.test('preserves correct subclass when someone returns a thenable', function (s2t) {
103 | var promise = promiseFinally(Subclass.resolve(), function () {
104 | return Promise.resolve(1);
105 | });
106 | s2t.ok(promise instanceof Subclass, 'promise is instanceof Subclass');
107 | s2t.equal(promise.constructor, Subclass, 'promise.constructor is Subclass');
108 |
109 | s2t.end();
110 | });
111 |
112 | st.test('invokes the subclass’ then', function (s2t) {
113 | Subclass.thenArgs.length = 0;
114 |
115 | var original = Subclass.resolve();
116 | promiseFinally(original, function () {});
117 |
118 | assertArray(s2t, original.thenArgs, 1);
119 | assertArray(s2t, Subclass.thenArgs, 1);
120 |
121 | s2t.end();
122 | });
123 |
124 | st.test('passes the original onFinally when not a function', function (s2t) {
125 | Subclass.thenArgs.length = 0;
126 |
127 | var original = Subclass.resolve();
128 | var sentinel = {};
129 | promiseFinally(original, sentinel);
130 |
131 | assertArray(s2t, original.thenArgs, 1, function (x) { s2t.ok(Array.isArray(x.args)); });
132 | assertArray(s2t, Subclass.thenArgs, 1, function (x) { s2t.ok(Array.isArray(x.args)); });
133 |
134 | assertArray(s2t, original.thenArgs[0].args, 2, function (x) { s2t.equal(x, sentinel); });
135 |
136 | s2t.end();
137 | });
138 |
139 | st.test('when onFinally is a function, passes thenFinally/catchFinally', function (s2t) {
140 | Subclass.thenArgs.length = 0;
141 |
142 | var sentinel = {};
143 | var original = Subclass.resolve(sentinel);
144 | var onFinallyArgs = [];
145 | var onFinally = function onFinallyHandler() {
146 | onFinallyArgs.push(Array.prototype.slice.call(arguments));
147 | return 42;
148 | };
149 | var promise = promiseFinally(original, onFinally);
150 |
151 | assertArray(s2t, original.thenArgs, 1, function (x) { s2t.ok(Array.isArray(x.args)); });
152 | assertArray(s2t, Subclass.thenArgs, 1, function (x) { s2t.ok(Array.isArray(x.args)); });
153 |
154 | var thenArgs = original.thenArgs[0];
155 | assertArray(s2t, thenArgs.args, 2, function (x) { s2t.equal(typeof x, 'function'); });
156 |
157 | s2t.deepEqual(onFinallyArgs, [], 'onFinally not yet called');
158 |
159 | s2t.test('thenFinally works as expected', function (s3t) {
160 | onFinallyArgs.length = 0;
161 |
162 | s3t.plan(6);
163 |
164 | assertArray(s3t, Subclass.thenArgs, 1);
165 |
166 | promise.then(function (x) {
167 | s3t.equal(x, sentinel, 'original resolution value provided');
168 | s3t.deepEqual(onFinallyArgs, [[]], 'onFinally called once with no args');
169 | assertArray(s3t, Subclass.thenArgs, 9);
170 | s3t.end();
171 | })['catch'](s3t.fail);
172 | });
173 |
174 | s2t.test('catchFinally works as expected', function (s3t) {
175 | onFinallyArgs.length = 0;
176 |
177 | s3t.plan(17);
178 | var thrown = { toString: function () { return 'thrown object'; } };
179 | var onFinallyRejects = function onFinallyThrower() {
180 | onFinally.apply(undefined, arguments);
181 | throw thrown;
182 | };
183 | Subclass.thenArgs.length = 0;
184 |
185 | var rejectedPromise = promiseFinally(original, onFinallyRejects);
186 |
187 | assertArray(s3t, Subclass.thenArgs, 1);
188 |
189 | var rejectedPromiseCatch = function (e) {
190 | s3t.equal(e, thrown, 'original rejection value provided');
191 | s3t.deepEqual(onFinallyArgs, [[]], 'onFinally called once with no args');
192 |
193 | assertArray(s3t, Subclass.thenArgs, 3);
194 | /*
195 | * 1) initial call with thenFinally/catchFinally
196 | * 2) rejectedPromise.then call
197 | * 3) rejectedPromise.then -> onFinally call
198 | */
199 | assertArray(s3t, Subclass.thenArgs[0].args, 2, function (x) { s3t.equal(typeof x, 'function'); });
200 |
201 | assertArray(s3t, Subclass.thenArgs[1].args, 2);
202 | s3t.deepEqual(Subclass.thenArgs[1].args, [s3t.fail, rejectedPromiseCatch], 'rejectedPromise.then call args');
203 |
204 | assertArray(s3t, Subclass.thenArgs[2].args, 2);
205 | s3t.equal(Subclass.thenArgs[2].args[0], undefined, 'final .then call gets no onFulfill');
206 | s3t.equal(typeof Subclass.thenArgs[2].args[1], 'function', 'final .then call gets an onReject');
207 |
208 | s3t.end();
209 | };
210 |
211 | rejectedPromise.then(s3t.fail, rejectedPromiseCatch)['catch'](s3t.fail);
212 | });
213 |
214 | s2t.end();
215 | });
216 |
217 | st.test('observable then calls', { todo: true }, function (s2t) {
218 | var mp1Value = {};
219 | var mp1 = Subclass.resolve(mp1Value);
220 | var mp2 = Subclass.resolve(42);
221 | var mp3 = Subclass.reject(mp1Value);
222 | var mp4 = Subclass.reject(42);
223 | mp3['catch'](function () {}); // avoid unhandled rejection warning
224 | mp4['catch'](function () {}); // avoid unhandled rejection warning
225 |
226 | s2t.test('resolved observable then calls', { todo: true }, function (s3t) {
227 | var orig = Subclass.thenArgs.length;
228 | s3t.plan(6);
229 | return promiseFinally(mp1, function () { return mp2; }).then(function () {
230 | assertArray(s3t, Subclass.thenArgs, orig + 5);
231 |
232 | var mp2Calls = Subclass.thenArgs.filter(function (c) { return c.promise === mp2; });
233 | assertArray(s3t, mp2Calls, 1);
234 | s3t.equal(mp2Calls[0].args[1], undefined, '`reject` is undefined');
235 | s3t.equal(mp2Calls[0].args[0](), mp1Value, '`resolve` produces `mp1Value`');
236 | });
237 | });
238 |
239 | s2t.test('rejected observable then calls', { todo: true }, function (s3t) {
240 | var orig = Subclass.thenArgs.length;
241 | s3t.plan(7);
242 | return promiseFinally(mp3, function () { return mp4; }).then(s3t.fail, function () {
243 | assertArray(s3t, Subclass.thenArgs, orig + 5);
244 |
245 | var mp4Calls = Subclass.thenArgs.filter(function (c) { return c.promise === mp4; });
246 | assertArray(s3t, mp4Calls, 1);
247 | s3t.equal(mp4Calls[0].args[1], undefined, '`reject` is undefined');
248 | var thrown = false;
249 | try {
250 | mp4Calls[0].args[0]();
251 | } catch (error) {
252 | thrown = true;
253 | s3t.equal(error, mp1Value, 'rejects to `mp1Value`');
254 | }
255 | s3t.ok(thrown, 'threw an error');
256 | });
257 | });
258 | });
259 | });
260 |
261 | return t.comment('tests completed');
262 | };
263 |
--------------------------------------------------------------------------------