Compare commits

..

52 Commits

Author SHA1 Message Date
jkunz 87b3356202 v4.2.4 2026-05-01 16:30:12 +00:00
jkunz d8b0da1826 fix(typescript): tighten TypeScript typings and modernize project tooling configuration 2026-05-01 16:30:12 +00:00
philkunz dc53984a77 4.2.3 2025-02-20 12:51:50 +00:00
philkunz a4d6c38a4f fix(test): Refactor test suite for improved readability and maintainability. 2025-02-20 12:51:50 +00:00
philkunz af0f406746 4.2.2 2025-01-23 20:09:58 +01:00
philkunz 73bf085376 fix(dependencies): Update @git.zone/tstest and @types/node dependencies to latest versions 2025-01-23 20:09:58 +01:00
philkunz 70c5f15b4c 4.2.1 2025-01-23 19:06:06 +01:00
philkunz ee9bbe021c fix(devDependencies): Update development dependencies to latest versions 2025-01-23 19:06:06 +01:00
philkunz 45d53eea31 4.2.0 2025-01-19 19:05:16 +01:00
philkunz d54b5790a3 feat(cumulativedeferred): Added subDefer method to CumulativeDeferred 2025-01-19 19:05:15 +01:00
philkunz a941ca69b3 4.1.0 2025-01-07 04:27:25 +01:00
philkunz 1afbfd8895 feat(core): Add fromCallback utility function for promisifying Node.js-style callback functions 2025-01-07 04:27:24 +01:00
philkunz 3802c03838 4.0.4 2024-06-23 23:46:43 +02:00
philkunz 8c18dcd0e6 fix(ci): Remove .gitlab-ci.yml and update dependencies and metadata 2024-06-23 23:46:42 +02:00
philkunz b8e7063bab update description 2024-05-29 14:15:32 +02:00
philkunz 85706e8a4e update tsconfig 2024-04-14 18:10:00 +02:00
philkunz 3a5ea9aa13 update npmextra.json: githost 2024-04-01 21:37:27 +02:00
philkunz 4d762353dd update npmextra.json: githost 2024-04-01 19:59:20 +02:00
philkunz f176e991a8 update npmextra.json: githost 2024-03-30 21:48:20 +01:00
philkunz 1f1e0e4ee1 4.0.3 2023-07-10 23:12:01 +02:00
philkunz 139d4cb56d fix(core): update 2023-07-10 23:12:00 +02:00
philkunz 8038e54f53 switch to new org scheme 2023-07-10 10:17:29 +02:00
philkunz 37284da2cc 4.0.2 2023-04-17 21:01:18 +02:00
philkunz b0772e2372 fix(core): update 2023-04-17 21:01:18 +02:00
philkunz f3eac4a5f7 4.0.1 2023-04-17 21:00:26 +02:00
philkunz fde31ec25d fix(core): update 2023-04-17 21:00:25 +02:00
philkunz 810b299caa 4.0.0 2023-04-04 20:54:35 +02:00
philkunz c71ac16090 BREAKING CHANGE(core): switch to esm 2023-04-04 20:54:35 +02:00
philkunz 91b52982c0 3.1.9 2023-04-04 20:35:54 +02:00
philkunz 97f8327fe9 fix(core): update 2023-04-04 20:35:54 +02:00
philkunz 42f40449ad 3.1.8 2023-04-04 20:22:57 +02:00
philkunz 960c23fecd fix(core): update 2023-04-04 20:22:57 +02:00
philkunz 40642fd6f6 3.1.7 2022-02-27 21:02:27 +01:00
philkunz 4c3565c618 fix(core): update 2022-02-27 21:02:27 +01:00
philkunz 1848006601 3.1.6 2021-05-31 13:02:23 +02:00
philkunz c761cc4460 fix(core): update 2021-05-31 13:02:22 +02:00
philkunz 9f6d88e162 3.1.5 2021-04-23 18:04:09 +00:00
philkunz 255d6415a5 fix(core): update 2021-04-23 18:04:08 +00:00
philkunz 84a8d8431c 3.1.4 2021-04-23 17:58:37 +00:00
philkunz 95d27d52a7 fix(core): update 2021-04-23 17:58:37 +00:00
philkunz 1363aa7957 3.1.3 2020-10-16 02:06:50 +00:00
philkunz 095ea36e5d fix(core): update 2020-10-16 02:06:50 +00:00
philkunz b6ab079de0 3.1.2 2020-10-16 01:39:19 +00:00
philkunz 734d5aca35 fix(core): update 2020-10-16 01:39:19 +00:00
philkunz 3b0052602f 3.1.1 2020-10-15 18:19:14 +00:00
philkunz 9693ec04d6 fix(core): update 2020-10-15 18:19:12 +00:00
philkunz 4ee932873c 3.1.0 2020-10-15 18:16:17 +00:00
philkunz 3bedd3581e feat(timeouts): allows the wrapping of promises in timeouts 2020-10-15 18:16:17 +00:00
philkunz ee3c1c6e79 3.0.9 2020-10-15 18:14:53 +00:00
philkunz 8c6ac181b0 fix(core): update 2020-10-15 18:14:53 +00:00
philkunz 51bce0b317 3.0.8 2020-10-15 18:13:58 +00:00
philkunz 374d73f87e fix(core): update 2020-10-15 18:13:58 +00:00
23 changed files with 8741 additions and 2081 deletions
+1 -3
View File
@@ -15,8 +15,6 @@ node_modules/
# builds
dist/
dist_web/
dist_serve/
dist_ts_web/
dist_*/
# custom
-119
View File
@@ -1,119 +0,0 @@
# gitzone ci_default
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
cache:
paths:
- .npmci_cache/
key: "$CI_BUILD_STAGE"
stages:
- security
- test
- release
- metadata
# ====================
# security stage
# ====================
mirror:
stage: security
script:
- npmci git mirror
tags:
- docker
- notpriv
snyk:
stage: security
script:
- npmci npm prepare
- npmci command npm install -g snyk
- npmci command npm install --ignore-scripts
- npmci command snyk test
tags:
- docker
- notpriv
# ====================
# test stage
# ====================
testStable:
stage: test
script:
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- priv
testBuild:
stage: test
script:
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci command npm run build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- notpriv
release:
stage: release
script:
- npmci node install stable
- npmci npm publish
only:
- tags
tags:
- docker
- notpriv
# ====================
# metadata stage
# ====================
codequality:
stage: metadata
allow_failure: true
script:
- npmci command npm install -g tslint typescript
- npmci npm install
- npmci command "tslint -c tslint.json ./ts/**/*.ts"
tags:
- docker
- priv
trigger:
stage: metadata
script:
- npmci trigger
only:
- tags
tags:
- docker
- notpriv
pages:
image: hosttoday/ht-docker-dbase:npmci
services:
- docker:stable-dind
stage: metadata
script:
- npmci command npm install -g @gitzone/tsdoc
- npmci npm prepare
- npmci npm install
- npmci command tsdoc
tags:
- docker
- notpriv
only:
- tags
artifacts:
expire_in: 1 week
paths:
- public
allow_failure: true
+39
View File
@@ -0,0 +1,39 @@
{
"@git.zone/cli": {
"projectType": "npm",
"module": {
"githost": "code.foss.global",
"gitscope": "push.rocks",
"gitrepo": "smartpromise",
"shortDescription": "promise and deferred utilities",
"description": "A TypeScript library for managing promises and Deferred constructs, simplifying asynchronous programming.",
"npmPackagename": "@push.rocks/smartpromise",
"license": "MIT",
"keywords": [
"promise",
"deferred",
"async",
"promisify",
"cumulative deferred",
"timeout",
"typescript",
"asynchronous",
"utilities"
]
},
"release": {
"registries": [
"https://verdaccio.lossless.digital",
"https://registry.npmjs.org"
],
"accessLevel": "public"
}
},
"@git.zone/tsdoc": {
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
},
"@ship.zone/szci": {
"npmGlobalTools": [],
"npmRegistryUrl": "registry.npmjs.org"
}
}
+11
View File
@@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "npm test",
"name": "Run npm test",
"request": "launch",
"type": "node-terminal"
}
]
}
+26
View File
@@ -0,0 +1,26 @@
{
"json.schemas": [
{
"fileMatch": ["/npmextra.json"],
"schema": {
"type": "object",
"properties": {
"npmci": {
"type": "object",
"description": "settings for npmci"
},
"gitzone": {
"type": "object",
"description": "settings for gitzone",
"properties": {
"projectType": {
"type": "string",
"enum": ["website", "element", "service", "npm", "wcc"]
}
}
}
}
}
}
]
}
-90
View File
@@ -1,90 +0,0 @@
# @pushrocks/smartpromise
simple promises and Deferred constructs
## Availabililty and Links
- [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartpromise)
- [gitlab.com (source)](https://gitlab.com/pushrocks/smartpromise)
- [github.com (source mirror)](https://github.com/pushrocks/smartpromise)
- [docs (typedoc)](https://pushrocks.gitlab.io/smartpromise/)
## Status for master
[![build status](https://gitlab.com/pushrocks/smartpromise/badges/master/build.svg)](https://gitlab.com/pushrocks/smartpromise/commits/master)
[![coverage report](https://gitlab.com/pushrocks/smartpromise/badges/master/coverage.svg)](https://gitlab.com/pushrocks/smartpromise/commits/master)
[![npm downloads per month](https://img.shields.io/npm/dm/@pushrocks/smartpromise.svg)](https://www.npmjs.com/package/@pushrocks/smartpromise)
[![Known Vulnerabilities](https://snyk.io/test/npm/@pushrocks/smartpromise/badge.svg)](https://snyk.io/test/npm/@pushrocks/smartpromise)
[![TypeScript](https://img.shields.io/badge/TypeScript->=%203.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
[![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-prettier-ff69b4.svg)](https://prettier.io/)
## Usage
Use TypeScript for best in class instellisense.
> Note: smartq uses native ES6 promises
> smartq does not repeat any native functions, so for things like .all() simply use Promise.all()
```typescript
import * as q from '@pushrocks/smartpromise'
// Deferred
// -----------------------------------------------
let myAsyncFunction = (): Promise<string> => {
let done = q.defer<string>() // returns your typical Deferred object
setTimeout(() => {
done.resolve('hi') // will throw type error for other types than string as argument ;)
},6000);
console.log(done.status) // logs "pending";
done.promise.then(() => {
console.log(done.status) // logs "fullfilled"
console.log(done.duration) // logs the milliseconds between instantiation and fullfillment
})
return done.promise
}
let myAsyncFunction2 = async () => {
let aString = await myAsyncFunction()
console.log(aString) // will log 'hi' to console
}
myAsyncFunction2();
// Resolved and Rejected promises
// ------------------------------------------------
q.resolvedPromise(`I'll get logged to console soon`)
.then(x => {
console.log(x)
})
q.rejectedPromise(`what a lovely error message`)
.then(() => {
console.log('This never makes it to console')
}/*, alternatively put a reject function here */)
.catch(err => {
console.log(err)
})
// Promisify (typed)
// ------------------------------------------------
let myCallbackedFunction = (someString: string, someNumber: number, cb) => {
cb(null, someString)
}
let myPromisedFunction = q.promisify(myCallbackFunction)
myPromisedFunction('helloThere', 2).then(x => {
console.log(x) // will log 'helloThere' to console
})
```
For further information read the linked docs at the top of this readme.
> MIT licensed | **&copy;** [Lossless GmbH](https://lossless.gmbh)
> | By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
[![repo-footer](https://lossless.gitlab.io/publicrelations/repofooter.svg)](https://maintainedby.lossless.com)
+130
View File
@@ -0,0 +1,130 @@
# Changelog
## 2026-05-01 - 4.2.4 - fix(typescript)
tighten TypeScript typings and modernize project tooling configuration
- replace custom async map implementation with Promise.all while preserving result order
- add explicit generic overloads and unknown-based typings across promise utilities and deferred classes
- enable noImplicitAny and update tests to use the current tstest tapbundle import
- refresh package and project config for newer git.zone tooling and npm release settings
## 2025-02-20 - 4.2.3 - fix(test)
Refactor test suite for improved readability and maintainability.
- Updated and cleaned up test cases for deferreds and promises.
- Reduced timeouts in tests for quicker execution.
- Added expectations for promise status and duration.
## 2025-02-20 - 4.2.3 - fix(test)
Refactor test suite for improved readability and maintainability.
- Updated and cleaned up test cases for deferreds and promises.
- Reduced timeouts in tests for quicker execution.
- Added expectations for promise status and duration.
## 2025-01-23 - 4.2.2 - fix(dependencies)
Update @git.zone/tstest and @types/node dependencies to latest versions
- Updated @git.zone/tstest from ^1.0.91 to ^1.0.92
- Updated @types/node from ^22.10.9 to ^22.10.10
## 2025-01-23 - 4.2.1 - fix(devDependencies)
Update development dependencies to latest versions
- Updated @git.zone/tstest to version ^1.0.91
- Updated @push.rocks/tapbundle to version ^5.5.6
- Updated @types/node to version ^22.10.9
## 2025-01-19 - 4.2.0 - feat(cumulativedeferred)
Added subDefer method to CumulativeDeferred
- Introduced `subDefer` method in CumulativeDeferred class to allow creating and adding new Deferreds easily.
## 2025-01-07 - 4.1.0 - feat(core)
Add fromCallback utility function for promisifying Node.js-style callback functions
- Added fromCallback function to convert Node.js-style callbacks into Promises.
## 2024-06-23 - 4.0.4 - fix(ci)
Remove .gitlab-ci.yml and update dependencies and metadata
- Removed .gitlab-ci.yml file
- Updated dependencies in package.json and npmextra.json
- Improved description and keywords for better package definition
## 2024-05-29 - 4.0.3 - Maintenance
Update project configuration and descriptions.
- Update project description
- Update tsconfig
- Update npmextra.json with githost field
## 2023-07-10 - 4.0.2 - Standards
Updates to adhere to new organizational standards.
- Switch to new org scheme
- Fix(core): update project settings
## 2023-04-17 - 4.0.0 - Major Release
Significant updates and switching to ESM.
- Fix(core): update project settings
## 2023-04-04 - 3.1.9 - Major Release
Breaking change: switch to ESM (ECMAScript Module).
## 2021-05-31 - 3.1.6 - Maintenance
Update project settings.
- Fix(core): update project settings
## 2021-04-23 - 3.1.4 - Maintenance
Update project settings.
- Fix(core): update project settings
## 2020-10-16 - 3.1.2 - Maintenance
Update project settings.
- Fix(core): update project settings
## 2020-10-15 - 3.0.9 - Feature
Added functionality for wrapping promises in timeouts.
- Feat(timeouts): allows the wrapping of promises in timeouts
## 2019-10-01 - 3.0.5 - Maintenance
Update project settings.
- Fix(core): update project settings
## 2019-03-26 - 3.0.0 - Major Release
Breaking change: remove util dependency.
- BREAKING CHANGE(remove util dependency and promisify functionality): update
## 2018-07-03 - 2.0.2 - Dependencies
Project now has zero dependencies.
- Fix(core): now has 0 dependencies
## 2018-03-16 - 1.1.8 - Major Release
Breaking change: switch scope to pushrocks.
- BREAKING CHANGE(scope): switch scope to pushrocks
## 2017-07-27 - 1.1.7 - Standards
Update to latest coding standards.
- Update to latest standards
## 2017-07-06 - 1.1.4 - Features and Improvements
Added polyfill and npmextra.json.
- Add polyfill
- Add npmextra.json
## 2017-07-06 - 1.1.2 - CI Maintenance
Updated continuous integration configuration.
- Update ci
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2026 Task Venture Capital GmbH <hello@task.vc>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+33 -10
View File
@@ -1,16 +1,39 @@
{
"npmci": {
"npmGlobalTools": [],
"npmAccessLevel": "public"
},
"gitzone": {
"@git.zone/cli": {
"projectType": "npm",
"module": {
"githost": "gitlab.com",
"gitscope": "pushrocks",
"githost": "code.foss.global",
"gitscope": "push.rocks",
"gitrepo": "smartpromise",
"shortDescription": "simple promises and Deferred constructs",
"npmPackagename": "@pushrocks/smartpromise",
"license": "MIT"
"shortDescription": "promise and deferred utilities",
"description": "A TypeScript library for managing promises and Deferred constructs, simplifying asynchronous programming.",
"npmPackagename": "@push.rocks/smartpromise",
"license": "MIT",
"keywords": [
"promise",
"deferred",
"async",
"promisify",
"cumulative deferred",
"timeout",
"typescript",
"asynchronous",
"utilities"
]
},
"release": {
"registries": [
"https://verdaccio.lossless.digital",
"https://registry.npmjs.org"
],
"accessLevel": "public"
}
},
"@git.zone/tsdoc": {
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
},
"@ship.zone/szci": {
"npmGlobalTools": [],
"npmRegistryUrl": "registry.npmjs.org"
}
}
-1718
View File
File diff suppressed because it is too large Load Diff
+48 -24
View File
@@ -1,42 +1,66 @@
{
"name": "@pushrocks/smartpromise",
"name": "@push.rocks/smartpromise",
"private": false,
"version": "3.0.7",
"description": "simple promises and Deferred constructs",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"version": "4.2.4",
"description": "A TypeScript library for managing promises and Deferred constructs, simplifying asynchronous programming.",
"main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts",
"scripts": {
"test": "(tstest test/)",
"build": "(tsbuild)"
"test": "tstest test/ --web",
"format": "gitzone format",
"build": "tsbuild --web",
"buildDocs": "tsdoc"
},
"repository": {
"type": "git",
"url": "git+ssh://git@gitlab.com/pushrocks/smartq.git"
"url": "https://code.foss.global/push.rocks/smartpromise.git"
},
"author": "Lossless GmbH",
"author": "Task Venture Capital GmbH <hello@task.vc>",
"license": "MIT",
"bugs": {
"url": "https://gitlab.com/pushrocks/smartq/issues"
"url": "https://code.foss.global/push.rocks/smartpromise/issues"
},
"homepage": "https://gitlab.com/pushrocks/smartq#README",
"dependencies": {},
"homepage": "https://code.foss.global/push.rocks/smartpromise",
"devDependencies": {
"@gitzone/tsbuild": "^2.1.17",
"@gitzone/tstest": "^1.0.24",
"@pushrocks/tapbundle": "^3.0.13",
"@types/node": "^12.7.5",
"tslint": "^5.20.0",
"tslint-config-prettier": "^1.18.0"
"@git.zone/tsbuild": "^4.4.0",
"@git.zone/tsrun": "^2.0.3",
"@git.zone/tstest": "^3.6.3",
"@types/node": "^25.6.0"
},
"files": [
"ts/*",
"ts_web/*",
"dist/*",
"dist_web/*",
"dist_ts_web/*",
"assets/*",
"ts/**/*",
"ts_web/**/*",
"dist/**/*",
"dist_*/**/*",
"dist_ts/**/*",
"dist_ts_web/**/*",
"assets/**/*",
"cli.js",
".smartconfig.json",
"license",
"npmextra.json",
"readme.md"
],
"browserslist": [
"last 1 chrome versions"
],
"type": "module",
"keywords": [
"promise",
"deferred",
"async",
"promisify",
"cumulative deferred",
"timeout",
"typescript",
"asynchronous",
"utilities"
],
"pnpm": {
"onlyBuiltDependencies": [
"esbuild",
"mongodb-memory-server",
"puppeteer"
]
}
}
+7860
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
+311
View File
@@ -0,0 +1,311 @@
# @push.rocks/smartpromise
A library for simple promises and Deferred constructs with TypeScript support.
## Install
```bash
npm install @push.rocks/smartpromise --save
```
This module is designed to be used with TypeScript for the best developer experience, providing type safety and IntelliSense in your IDE.
## Usage
`@push.rocks/smartpromise` simplifies the use of promises and deferred constructs in TypeScript, offering a set of utility functions that extend native Promise capabilities in specific scenarios. This guide walks you through its functionalities, showcasing how to leverage this library in a TypeScript project.
### Setting Up Your Project
Ensure your TypeScript project is configured to support ES Module syntax. In your `tsconfig.json`, you should have:
```json
{
"compilerOptions": {
"module": "ESNext",
"target": "ESNext",
"moduleResolution": "node"
}
}
```
### Basic Usage
#### Creating a Deferred
Deferred objects in `@push.rocks/smartpromise` give you more control over your promises, allowing for manual resolution or rejection beyond the initial executor function.
```typescript
import { defer } from '@push.rocks/smartpromise';
async function exampleDeferred(): Promise<string> {
const myDeferred = defer<string>();
// Simulate an async task
setTimeout(() => {
myDeferred.resolve('Hello, Deferred!');
}, 1000);
return myDeferred.promise;
}
exampleDeferred().then((result) => console.log(result));
```
#### Cumulative Deferred
For scenarios where multiple asynchronous tasks need to be tracked collectively before proceeding, use `CumulativeDeferred`. It waits for all added promises to resolve.
```typescript
import { cumulativeDefer } from '@push.rocks/smartpromise';
async function exampleCumulativeDeferred() {
const myCumulativeDeferred = cumulativeDefer();
for (let i = 0; i < 5; i++) {
myCumulativeDeferred.addPromise(new Promise((resolve) => setTimeout(resolve, 1000 * i)));
}
await myCumulativeDeferred.promise;
console.log('All tasks completed!');
}
exampleCumulativeDeferred();
```
#### Utilizing Resolved and Rejected Promises
Quickly create already resolved or rejected promises for testing or initialization purposes.
```typescript
import { resolvedPromise, rejectedPromise } from '@push.rocks/smartpromise';
resolvedPromise('immediately resolved').then(console.log);
rejectedPromise('immediately rejected').catch(console.error);
```
### Advanced Use Cases
#### Promisify Callback Functions
`@push.rocks/smartpromise` does not directly provide a `promisify` function like Node.js `util` module, but you can easily integrate existing functions or use third-party libraries to convert callback-based functions into promises.
#### Handling Timeouts and Continuations
Managing timeouts or long-running promises gets easier with helper functions.
```typescript
import { timeoutWrap, timeoutAndContinue } from '@push.rocks/smartpromise';
async function exampleTimeout() {
const myPromise = new Promise((resolve) => setTimeout(() => resolve('Done!'), 2000));
// Will reject if the promise does not resolve within 1 second
try {
const result = await timeoutWrap(myPromise, 1000);
console.log(result);
} catch (error) {
console.error('Promise timed out');
}
// Continues after 1 second, regardless of whether the promise has resolved
const result = await timeoutAndContinue(myPromise, 1000);
console.log(result); // May log `null` if the original promise did not resolve in time
}
exampleTimeout();
```
#### Map and Reduce Asynchronous Functions
Suppose you have a collection of items that you need to process asynchronously. You can use the `map` function to apply an asynchronous function to each item in the array and wait for all the promises to resolve.
```typescript
import { map } from '@push.rocks/smartpromise';
async function processData(items: string[]): Promise<string[]> {
return map(items, async (item) => {
// Simulate an async operation
await new Promise((resolve) => setTimeout(resolve, 100));
return item.toUpperCase();
});
}
processData(['hello', 'world']).then(console.log);
```
### Handling Complex Promise Scenarios
#### Managing Multiple Deferreds
If you have multiple deferred objects and you want to manage them collectively, you can do so using the `CumulativeDeferred` class. This is especially useful when you have a dynamic number of deferreds to resolve.
```typescript
import { defer, cumulativeDefer } from '@push.rocks/smartpromise';
async function exampleComplexDeferred(): Promise<void> {
const task1 = defer<string>();
const task2 = defer<string>();
const cumulative = cumulativeDefer();
cumulative.addPromise(task1.promise);
cumulative.addPromise(task2.promise);
// Simulate async tasks
setTimeout(() => task1.resolve('Task 1 complete'), 1000);
setTimeout(() => task2.resolve('Task 2 complete'), 2000);
await cumulative.promise;
console.log('All tasks completed');
}
exampleComplexDeferred();
```
### Other Utilities
#### Race Condition Handling with `getFirstTrueOrFalse`
This helper function resolves immediately when one of the provided promises resolves to `true` or when all promises are resolved to `false`.
```typescript
import { getFirstTrueOrFalse } from '@push.rocks/smartpromise';
async function exampleRaceCondition() {
const task1 = new Promise<boolean>((resolve) => setTimeout(() => resolve(true), 1000));
const task2 = new Promise<boolean>((resolve) => setTimeout(() => resolve(false), 2000));
const result = await getFirstTrueOrFalse([task1, task2]);
console.log(result); // Outputs: true
}
exampleRaceCondition();
```
### Complete Use Case
Let's create a comprehensive example that showcases multiple features of `@push.rocks/smartpromise`.
```typescript
import {
defer,
cumulativeDefer,
resolvedPromise,
rejectedPromise,
timeoutWrap,
timeoutAndContinue,
map,
getFirstTrueOrFalse,
} from '@push.rocks/smartpromise';
async function completeUseCaseExample() {
console.log('Starting Complete Use Case Example!');
// Using Deferred
const myDeferred = defer<string>();
setTimeout(() => myDeferred.resolve('Deferred Resolved!'), 500);
console.log(await myDeferred.promise); // Outputs: "Deferred Resolved!"
// Using Cumulative Deferred
const myCumulativeDeferred = cumulativeDefer();
myCumulativeDeferred.addPromise(new Promise((resolve) => setTimeout(() => resolve('Task 1'), 1000)));
myCumulativeDeferred.addPromise(new Promise((resolve) => setTimeout(() => resolve('Task 2'), 2000)));
await myCumulativeDeferred.promise;
console.log('All cumulative tasks completed');
// Using Resolved and Rejected Promises
await resolvedPromise('Instant Resolve').then(console.log); // Outputs: "Instant Resolve"
await rejectedPromise('Instant Reject').catch(console.error); // Outputs: "Instant Reject"
// Using timeoutWrap
try {
const delayedPromise = new Promise((resolve) => setTimeout(() => resolve('Finished'), 3000));
const result = await timeoutWrap(delayedPromise, 1000);
console.log(result);
} catch (e) {
console.error('Timeout occurred'); // Outputs: "Timeout occurred"
}
// Using timeoutAndContinue
const resultContinue = await timeoutAndContinue(
new Promise((resolve) => setTimeout(() => resolve('Finished eventually'), 3000)),
1000
);
console.log(resultContinue); // Outputs: null (since it didn't resolve in 1 second)
// Using Map
const items = ['a', 'b', 'c'];
const processedItems = await map(items, async (item) => {
await new Promise((resolve) => setTimeout(resolve, 500));
return item.toUpperCase();
});
console.log(processedItems); // Outputs: ['A', 'B', 'C']
// Using getFirstTrueOrFalse
const raceResults = await getFirstTrueOrFalse([
new Promise<boolean>((resolve) => setTimeout(() => resolve(false), 1000)),
new Promise<boolean>((resolve) => setTimeout(() => resolve(true), 2000)),
]);
console.log(raceResults); // Outputs: true
console.log('Complete Use Case Example Finished!');
}
completeUseCaseExample();
```
### Testing Your Code
Testing is crucial to ensure the reliability of your asynchronous workflows. You can write tests using the `@push.rocks/tapbundle` library to create unit tests for your promises and deferred constructs.
```typescript
import { tap, expect } from '@push.rocks/tapbundle';
import * as smartpromise from '@push.rocks/smartpromise';
tap.test('should resolve a deferred promise', async () => {
const deferred = smartpromise.defer<string>();
deferred.resolve('Resolved!');
const result = await deferred.promise;
expect(result).toEqual('Resolved!');
});
tap.test('should timeout a long-running promise', async () => {
const longRunningPromise = new Promise((resolve) => setTimeout(resolve, 2000));
try {
await smartpromise.timeoutWrap(longRunningPromise, 1000);
} catch (err) {
expect(err.message).toEqual('timeout');
}
});
tap.test('should map async function to an array', async () => {
const inputArray = ['a', 'b'];
const result = await smartpromise.map(inputArray, async (item) => item.toUpperCase());
expect(result).toEqual(['A', 'B']);
});
tap.start();
```
By following this guide and using the examples provided, you should be able to effectively use `@push.rocks/smartpromise` for managing promises and deferred constructs in your TypeScript project. The library's extensive utility functions, combined with TypeScript support, make it a powerful tool for modern asynchronous programming needs.
Explore the full range of features and feel free to read through the source code to learn more about the implementation details. Happy coding!
## License and Legal Information
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
### Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
### Company Information
Task Venture Capital GmbH
Registered at District court Bremen HRB 35230 HB, Germany
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
+27
View File
@@ -0,0 +1,27 @@
import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as smartpromise from '../ts/index.js';
tap.test('should return a Deferred for .defer()', async () => {
const myDeferred = smartpromise.defer();
myDeferred.resolve();
await myDeferred.promise;
});
tap.test('should let types flow through the Promise', async () => {
const myString = 'someString';
const myDeferred = smartpromise.defer<string>();
myDeferred.resolve(myString);
const result = await myDeferred.promise;
expect(result).toEqual('someString');
});
tap.test('should map callbacks', async () => {
const inputArray = ['hi', 'awesome'];
const myPromisified = async (myInput: string) => {
return myInput;
};
const result = await smartpromise.map(inputArray, myPromisified);
expect(result).toEqual(inputArray);
});
export default tap.start();
+57
View File
@@ -0,0 +1,57 @@
import * as smartpromise from '../ts/index.js';
import { tap, expect } from '@git.zone/tstest/tapbundle';
tap.test('simple deferred should resolve with correct value', async () => {
const done = smartpromise.defer<string>();
let result: string | undefined;
done.promise.then((stringArg) => {
result = stringArg;
});
done.resolve('hello');
await done.promise;
expect(result).toEqual('hello');
});
tap.test('deferred should work with async functions and track status/duration', async () => {
const myAsyncFunction = async (): Promise<string> => {
const done = smartpromise.defer<string>();
setTimeout(() => {
done.resolve('hi');
}, 100); // reduced timeout for testing
expect(done.status).toEqual('pending');
await done.promise;
expect(done.status).toEqual('fulfilled');
expect(done.duration).toBeGreaterThan(0);
return done.promise;
};
const result = await myAsyncFunction();
expect(result).toEqual('hi');
});
tap.test('resolvedPromise should resolve immediately with correct value', async () => {
const message = `I'll get logged to console soon`;
const result = await smartpromise.resolvedPromise(message);
expect(result).toEqual(message);
});
tap.test('rejectedPromise should reject with correct error message', async () => {
const errorMessage = 'what a lovely error message';
let caught = false;
try {
await smartpromise.rejectedPromise(errorMessage);
} catch (err) {
caught = true;
expect(err).toEqual(errorMessage);
}
expect(caught).toEqual(true);
});
export default tap.start();
-30
View File
@@ -1,30 +0,0 @@
import { expect, tap } from '@pushrocks/tapbundle';
import * as smartpromise from '../ts/index';
tap.test('should return a Deferred for .defer()', async () => {
const myDeferred = smartpromise.defer();
const expectPromise = expect(myDeferred.promise).to.eventually.be.fulfilled;
myDeferred.resolve();
return expectPromise;
});
tap.test('should let types flow through the Promise', async () => {
const myString = 'someString';
const myDeferred = smartpromise.defer<string>();
const expectPromise = expect(myDeferred.promise).to.eventually.equal('someString');
myDeferred.resolve(myString);
return expectPromise;
});
tap.test('should map callbacks', async () => {
const inputArray = ['hi', 'awesome'];
const myPromisified = async myInput => {
return myInput;
};
const expectPromise = expect(smartpromise.map(inputArray, myPromisified)).to.eventually.deep.equal(
inputArray
);
return expectPromise;
});
tap.start();
+8
View File
@@ -0,0 +1,8 @@
/**
* autocreated commitinfo by @push.rocks/commitinfo
*/
export const commitinfo = {
name: '@push.rocks/smartpromise',
version: '4.2.4',
description: 'A TypeScript library for managing promises and Deferred constructs, simplifying asynchronous programming.'
}
+68 -67
View File
@@ -1,67 +1,26 @@
export interface IResolve<T> {
(value?: T | PromiseLike<T>): void;
}
import { defer } from './smartpromise.classes.deferred.js';
export interface IReject {
(reason?: any): void;
}
export type TDeferredStatus = 'pending' | 'fulfilled' | 'rejected';
export class Deferred<T> {
public promise: Promise<T>;
public resolve: IResolve<T>;
public reject: IReject;
public status: TDeferredStatus;
public startedAt: number;
public stoppedAt: number;
public get duration(): number {
if (this.stoppedAt) {
return this.stoppedAt - this.startedAt;
} else {
return Date.now() - this.startedAt;
}
}
constructor() {
this.promise = new Promise<T>((resolve, reject) => {
this.resolve = (valueArg: T | PromiseLike<T>) => {
this.status = 'fulfilled';
this.stoppedAt = Date.now();
resolve(valueArg);
};
this.reject = (reason: any) => {
this.status = 'rejected';
this.stoppedAt = Date.now();
reject(reason);
};
this.startedAt = Date.now();
this.status = 'pending';
});
}
}
export const defer = <T>() => {
return new Deferred<T>();
};
export * from './smartpromise.classes.cumulativedeferred.js';
export * from './smartpromise.classes.deferred.js';
export function resolvedPromise(): Promise<void>;
export function resolvedPromise<T>(value: T): Promise<T>;
/**
* Creates a new resolved promise for the provided value.
*/
export const resolvedPromise = <T>(value?: T): Promise<T> => {
export function resolvedPromise<T>(value?: T): Promise<T | void> {
return Promise.resolve(value);
};
}
/**
* Creates a new rejected promise for the provided reason.
*/
export const rejectedPromise = (err) => {
export const rejectedPromise = (err: unknown): Promise<never> => {
return Promise.reject(err);
};
interface IAsyncFunction<T> {
(someArg: T): Promise<T>;
interface IAsyncFunction<TInput, TOutput> {
(someArg: TInput): Promise<TOutput>;
}
/**
@@ -70,25 +29,67 @@ interface IAsyncFunction<T> {
* @param inputArg
* @param functionArg
*/
export const map = async <T>(inputArg: T[], functionArg: IAsyncFunction<T>) => {
const promiseArray: Promise<any>[] = [];
const resultArray = [];
for (const item of inputArg) {
const promise: Promise<any> = functionArg(item);
promiseArray.push(promise);
promise.then((x) => {
resultArray.push(x);
});
}
await Promise.all(promiseArray);
return resultArray;
export const map = async <TInput, TOutput = TInput>(
inputArg: TInput[],
functionArg: IAsyncFunction<TInput, TOutput>
): Promise<TOutput[]> => {
return Promise.all(inputArg.map(functionArg));
};
export const timeoutWrap = (ms, promise) => {
return new Promise((resolve, reject) => {
export const timeoutWrap = async <T = unknown>(
promiseArg: Promise<T>,
timeoutInMsArg: number,
rejectArg = true
): Promise<T | null> => {
return new Promise<T | null>((resolve, reject) => {
setTimeout(() => {
if (rejectArg) {
reject(new Error('timeout'));
}, ms);
promise.then(resolve, reject);
} else {
resolve(null);
}
}, timeoutInMsArg);
promiseArg.then(resolve, reject);
});
};
export const timeoutAndContinue = async <T = unknown>(
promiseArg: Promise<T>,
timeoutInMsArg = 60000
): Promise<T | null> => {
return timeoutWrap(promiseArg, timeoutInMsArg, false);
};
export const getFirstTrueOrFalse = async (promisesArg: Promise<boolean>[]): Promise<boolean> => {
const done = defer<boolean>();
for (const promiseArg of promisesArg) {
promiseArg.then((resultArg) => {
if (resultArg === true) {
done.resolve(true);
}
});
}
Promise.all(promisesArg).then(() => {
done.resolve(false);
});
return done.promise;
};
/**
* Converts a Node.js-style callback-based function into a Promise.
* @param fn The function that expects a callback.
* @returns A Promise that resolves with the result of the function or rejects with an error.
*/
export const fromCallback = <T>(
fn: (callback: (err: NodeJS.ErrnoException | null, result?: T) => void) => void
): Promise<T> => {
return new Promise((resolve, reject) => {
fn((err, result) => {
if (err) {
reject(err); // Reject the promise with the error
} else {
resolve(result as T); // Resolve the promise with the result
}
});
});
};
@@ -0,0 +1,31 @@
import { defer } from './smartpromise.classes.deferred.js';
export class CumulativeDeferred {
private accumulatedPromises: Promise<unknown>[] = [];
private deferred = defer<void>();
public promise = this.deferred.promise;
constructor() {
setTimeout(async () => {
while (this.accumulatedPromises.length > 0) {
const poppedPromise = this.accumulatedPromises.shift();
await poppedPromise;
}
this.deferred.resolve();
}, 0);
}
public subDefer() {
const done = defer<void>();
this.addPromise(done.promise);
return done;
}
public addPromise(promiseArg: Promise<unknown>): void {
this.accumulatedPromises.push(promiseArg);
}
}
export const cumulativeDefer = () => {
return new CumulativeDeferred();
};
+54
View File
@@ -0,0 +1,54 @@
export interface IResolve<T> {
(value?: T | PromiseLike<T>): void;
}
export interface IReject {
(reason?: unknown): void;
}
export type TDeferredStatus = 'pending' | 'fulfilled' | 'rejected';
export class Deferred<T> {
public promise: Promise<T>;
public resolve!: IResolve<T>;
public reject!: IReject;
public status!: TDeferredStatus;
public claimed = false;
public claim() {
if (this.claimed) {
throw new Error('Deferred already claimed');
}
this.claimed = true;
}
public startedAt!: number;
public stoppedAt!: number;
public get duration(): number {
if (this.stoppedAt) {
return this.stoppedAt - this.startedAt;
} else {
return Date.now() - this.startedAt;
}
}
constructor() {
this.promise = new Promise<T>((resolve, reject) => {
this.resolve = (valueArg?: T | PromiseLike<T>) => {
this.status = 'fulfilled';
this.stoppedAt = Date.now();
resolve(valueArg as T | PromiseLike<T>);
};
this.reject = (reason?: unknown) => {
this.status = 'rejected';
this.stoppedAt = Date.now();
reject(reason);
};
this.startedAt = Date.now();
this.status = 'pending';
});
}
}
export const defer = <T = void>() => {
return new Deferred<T>();
};
+14
View File
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"useDefineForClassFields": false,
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"noImplicitAny": true,
"esModuleInterop": true,
"verbatimModuleSyntax": true,
"types": ["node"]
},
"exclude": ["dist_*/**/*.d.ts"]
}
-17
View File
@@ -1,17 +0,0 @@
{
"extends": ["tslint:latest", "tslint-config-prettier"],
"rules": {
"semicolon": [true, "always"],
"no-console": false,
"ordered-imports": false,
"object-literal-sort-keys": false,
"member-ordering": {
"options":{
"order": [
"static-method"
]
}
}
},
"defaultSeverity": "warning"
}