Skip to content

Commit

Permalink
exported promisify method;
Browse files Browse the repository at this point in the history
added `CanceledError.rethrow` method;
all static methods lazily bound to the constructor context;
  • Loading branch information
DigitalBrainJS committed Dec 12, 2020
1 parent 895d0d6 commit 8c9746b
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 78 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

For changes before version 0.4.0, please see the commit history

## [0.10.8] - 2020-12-13

### Added
- exported `promisify` method;
- `CanceledError.rethrow` method;

### Updated
- all static methods lazily bound to the constructor context;

## [0.10.7] - 2020-12-12

### Added
Expand Down
52 changes: 30 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,33 +406,37 @@ Check out this [live demo](https://codesandbox.io/s/infallible-ardinghelli-7r6o8
}
````
#### React functional component
Using hooks and CPromise `cancel` method [Live Demo](https://codesandbox.io/s/react-cpromise-fetch-kydim?file=/src/MyComponent.js):
Using hooks and CPromise `cancel` method [Live Demo](https://codesandbox.io/s/react-cpromise-fetch-promisify-forked-3h4v2?file=/src/MyComponent.js):
````jsx
import React, { useEffect, useState } from "react";
import CPromise from "c-promise2";
import { CPromise, CanceledError } from "c-promise2";
import cpFetch from "cp-fetch";

function MyComponent(props) {
const [text, setText] = useState("fetching...");

useEffect(() => {
console.log("mount");
const promise = cpFetch(props.url)
.then((response) => response.json())
.then((value) => CPromise.delay(1000, value))
.then((json) => setText(`Success: ${JSON.stringify(json)}`))
.canceled() // catch CanceledError
.catch((err) => {
setText(`Failed: ${err}`);
});

return () => {
console.log("unmount");
promise.cancel();
};
}, [props.url]);

return <p>{text}</p>;
const [text, setText] = useState("fetching...");

useEffect(() => {
console.log("mount");
const promise = CPromise.from(function* () {
try {
const response = yield cpFetch(props.url);
const json = yield response.json();
yield CPromise.delay(1000);
setText(`Success: ${JSON.stringify(json)}`);
} catch (err) {
console.warn(err);
CanceledError.rethrow(err);
setText(`Failed: ${err}`);
}
});

return () => {
console.log("unmount");
promise.cancel();
};
}, [props.url]);

return <p>{text}</p>;
}
````
#### React class component with CPromise decorators
Expand Down Expand Up @@ -824,6 +828,10 @@ label decorator
### CPromise.progress : <code>function</code>
progress decorator

**Kind**: static property of [<code>CPromise</code>](#module_CPromise)
<a name="module_CPromise.promisify"></a>

### CPromise.promisify : <code>function</code>
**Kind**: static property of [<code>CPromise</code>](#module_CPromise)
<a name="module_CPromise..CPromise"></a>

Expand Down
48 changes: 26 additions & 22 deletions jsdoc2md/README.hbs.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,33 +406,37 @@ Check out this [live demo](https://codesandbox.io/s/infallible-ardinghelli-7r6o8
}
````
#### React functional component
Using hooks and CPromise `cancel` method [Live Demo](https://codesandbox.io/s/react-cpromise-fetch-kydim?file=/src/MyComponent.js):
Using hooks and CPromise `cancel` method [Live Demo](https://codesandbox.io/s/react-cpromise-fetch-promisify-forked-3h4v2?file=/src/MyComponent.js):
````jsx
import React, { useEffect, useState } from "react";
import CPromise from "c-promise2";
import { CPromise, CanceledError } from "c-promise2";
import cpFetch from "cp-fetch";

function MyComponent(props) {
const [text, setText] = useState("fetching...");

useEffect(() => {
console.log("mount");
const promise = cpFetch(props.url)
.then((response) => response.json())
.then((value) => CPromise.delay(1000, value))
.then((json) => setText(`Success: ${JSON.stringify(json)}`))
.canceled() // catch CanceledError
.catch((err) => {
setText(`Failed: ${err}`);
});

return () => {
console.log("unmount");
promise.cancel();
};
}, [props.url]);

return <p>{text}</p>;
const [text, setText] = useState("fetching...");

useEffect(() => {
console.log("mount");
const promise = CPromise.from(function* () {
try {
const response = yield cpFetch(props.url);
const json = yield response.json();
yield CPromise.delay(1000);
setText(`Success: ${JSON.stringify(json)}`);
} catch (err) {
console.warn(err);
CanceledError.rethrow(err);
setText(`Failed: ${err}`);
}
});

return () => {
console.log("unmount");
promise.cancel();
};
}, [props.url]);

return <p>{text}</p>;
}
````
#### React class component with CPromise decorators
Expand Down
29 changes: 29 additions & 0 deletions lib/c-promise.js
Original file line number Diff line number Diff line change
Expand Up @@ -1891,6 +1891,31 @@ Object.entries(decorators).forEach(([name, initializer]) => {
})
})

const lazyBindMethods = (obj, props) => {
props.forEach(prop => {
const symbol = Symbol(`${prop}Bound`);
const {value: fn} = Object.getOwnPropertyDescriptor(obj, prop);

Object.defineProperty(obj, prop, {
get: function () {
return this[symbol] || (this[symbol] = fn.bind(this))
},

set: function (v) {
throw Error(`Can not rewrite method ${prop} with ${v}`);
},

enumerable: true,
configurable: true
})
})
}

lazyBindMethods(CPromise, Object.getOwnPropertyNames(CPromise).map(prop => {
const {value} = Object.getOwnPropertyDescriptor(CPromise, prop);
return typeof value === 'function' ? prop : '';
}).filter(Boolean));

exports.CPromise = CPromise;
/**
* CanceledError class
Expand Down Expand Up @@ -1961,3 +1986,7 @@ exports.canceled = CPromise.canceled;
* @type {Function}
*/
exports.progress = CPromise.progress;
/**
* @type {Function}
*/
exports.promisify = CPromise.promisify;
92 changes: 58 additions & 34 deletions lib/canceled-error.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,39 @@
* @module CanceledError
*/

const _scope= Symbol('scope');
const _errors= Symbol('errors');
const _code= Symbol('code');
const _scope = Symbol('scope');
const _errors = Symbol('errors');
const _code = Symbol('code');

/**
* CanceledError
*/

class CanceledError extends Error{
class CanceledError extends Error {
/**
* Constructs a new error instance
* @param {String} reason
*/
constructor(reason= E_REASON_CANCELED) {
constructor(reason = E_REASON_CANCELED) {
super();
this.name= this.constructor.name;
const registered= this.constructor[_errors][reason];
if(registered){
this.message= registered;
this[_code]= reason;
}else{
this.message= reason;
this.name = this.constructor.name;
const registered = this.constructor[_errors][reason];
if (registered) {
this.message = registered;
this[_code] = reason;
} else {
this.message = reason;
}

this[_scope]= null;
this[_scope] = null;
}

/**
* get promise scope where the error was raised
* @returns {CPromise|null}
*/

get scope(){
get scope() {
return this[_scope];
}

Expand All @@ -43,13 +43,13 @@ class CanceledError extends Error{
* @returns {string}
*/

get code(){
get code() {
return this[_code] || '';
}

set scope(scope){
if(!this[_scope]){
this[_scope]= scope;
set scope(scope) {
if (!this[_scope]) {
this[_scope] = scope;
return;
}
throw Error('Scope has been already set');
Expand Down Expand Up @@ -87,30 +87,54 @@ class CanceledError extends Error{
* @returns {boolean}
*/

static isCanceledError(thing){
static isCanceledError(thing) {
return thing instanceof this;
}

static registerErrors(errors){
return Object.entries(errors).reduce((acc, [name, message])=>{
Object.defineProperty(this, name, {
value: name
});
this[_errors][name]= message;
acc[name]= name;
return acc;
}, {});
static registerErrors(errors) {
return Object.entries(errors).reduce((acc, [name, message]) => {
Object.defineProperty(this, name, {
value: name
});
this[_errors][name] = message;
acc[name] = name;
return acc;
}, {});
}

/**
* Rethrow the canceled error with optional codes matching
* @param err an error to assert
* @param {...String} [codes]
* @throws CanceledError
*/

static rethrow(err, ...codes) {
const {length} = codes;
if (!(err instanceof CanceledError)) {
return;
}
if (!length) {
throw err;
} else {
const {code} = err;
for (let i = 0; i < length; i++) {
if (codes[i] === code) {
throw err;
}
}
}
}
}

CanceledError[_errors]= {};
CanceledError[_errors] = {};

const {E_REASON_CANCELED}= CanceledError.registerErrors({
E_REASON_CANCELED : 'canceled',
E_REASON_TIMEOUT : 'timeout',
E_REASON_DISPOSED : 'disposed'
const {E_REASON_CANCELED} = CanceledError.registerErrors({
E_REASON_CANCELED: 'canceled',
E_REASON_TIMEOUT: 'timeout',
E_REASON_DISPOSED: 'disposed'
});

module.exports= {
module.exports = {
CanceledError
};

0 comments on commit 8c9746b

Please sign in to comment.