Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

async-caf

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

async-caf - npm Package Compare versions

Comparing version 1.1.0 to 2.0.0

lib/abortcontroller-polyfill-modified.js

134

dist/caf.js
/*! caf.js
v1.1.0 (c) 2018 Kyle Simpson
v2.0.0 (c) 2018 Kyle Simpson
MIT License: http://getify.mit-license.org
*/
// polyfill for AbortController, adapted from: https://github.com/mo/abortcontroller-polyfill
/* istanbul ignore next */
(function UMD(context,definition){
if (typeof define === "function" && define.amd) { define(definition); }
else { definition(context); }
})(typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : this,function DEF(context){
"use strict";
if (context.AbortController) {
return;
}
class Emitter {
constructor() {
const delegate = typeof document != "undefined" ?
document.createDocumentFragment() :
{};
const methods = ["addEventListener", "dispatchEvent", "removeEventListener"];
methods.forEach(method =>
this[method] = (...args) => delegate[method](...args)
);
}
}
class AbortSignal extends Emitter {
constructor() {
super();
this.aborted = false;
}
toString() {
return "[object AbortSignal]";
}
}
class AbortController {
constructor() {
this.signal = new AbortSignal();
}
abort() {
this.signal.aborted = true;
try {
this.signal.dispatchEvent(new Event("abort"));
} catch (e) {
if (typeof document != "undefined") {
// For Internet Explorer 11:
const event = document.createEvent("Event");
event.initEvent("abort", false, true);
this.signal.dispatchEvent(event);
}
}
}
toString() {
return "[object AbortController]";
}
}
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
// These are necessary to make sure that we get correct output for:
// Object.prototype.toString.call(new AbortController())
AbortController.prototype[Symbol.toStringTag] = "AbortController";
AbortSignal.prototype[Symbol.toStringTag] = "AbortSignal";
}
context.AbortController = AbortController;
context.AbortSignal = AbortSignal;
});
(function UMD(name,context,definition){
/* istanbul ignore next */if (typeof define === "function" && define.amd) { define(definition); }
/* istanbul ignore next */else if (typeof module !== "undefined" && module.exports) { module.exports = definition(); }
/* istanbul ignore next */else if (typeof module !== "undefined" && module.exports) { module.exports = definition(name,context); }
/* istanbul ignore next */else { context[name] = definition(name,context); }

@@ -13,4 +81,21 @@ })("CAF",this,function DEF(name,context){

cancelToken.prototype.cancel = cancel;
cancelToken.prototype.listen = listen;
class cancelSignal extends AbortSignal {
constructor() {
super();
this.pr = new Promise((_,rej)=>this.rej = rej);
this.pr.catch(_=>1); // silence unhandled rejection warnings
}
}
class cancelToken extends AbortController {
constructor() {
super();
this.signal = new cancelSignal();
}
abort(reason) {
super.abort();
this.signal.rej(reason);
}
}
CAF.cancelToken = cancelToken;

@@ -26,13 +111,11 @@

return function instance(cancelToken,...args){
var trigger;
var canceled = new Promise(function c(_,rej){
trigger = rej;
});
var { it, pr } = _runner.call(this,generatorFn,cancelToken,...args);
cancelToken.listen(function onCancel(reason){
try { var ret = it.return(); } catch (err) {}
trigger(ret.value !== undefined ? ret.value : reason);
it = pr = trigger = null;
var cancel = cancelToken.pr.catch(function onCancel(reason){
try {
var ret = it.return();
throw ret.value !== undefined ? ret.value : reason;
}
finally { it = pr = cancel = null; }
});
var race = Promise.race([ pr, canceled ]);
var race = Promise.race([ pr, cancel ]);
race.catch(_=>1); // silence unhandled rejection warnings

@@ -43,27 +126,2 @@ return race;

function cancelToken() {
this.canceled = false;
this.cancelationReason = undefined;
this.listeners = [];
}
function cancel(reason) {
this.cancelationReason = reason;
this.canceled = true;
// note: running in LIFO instead of FIFO order
// to ensure that cascaded cancelations run in
// expected order
while (this.listeners.length > 0) {
let cb = this.listeners.pop();
try { cb(reason); } catch (err) {}
}
}
function listen(cb) {
if (this.canceled) {
try { cb(this.cancelationReason); } catch (err) {}
}
else {
this.listeners.push(cb);
}
}
// thanks to Benjamin Gruenbaum (@benjamingr on GitHub) for

@@ -70,0 +128,0 @@ // big improvements here!

{
"name": "async-caf",
"version": "1.1.0",
"version": "2.0.0",
"description": "Wrapper for generators as cancelable async functions",

@@ -5,0 +5,0 @@ "main": "./dist/caf.js",

# Cancelable Async Functions (CAF)
[![Build Status](https://travis-ci.org/getify/caf.svg?branch=master)](https://travis-ci.org/getify/caf)
[![Build Status](https://travis-ci.org/getify/CAF.svg?branch=master)](https://travis-ci.org/getify/CAF)
[![npm Module](https://badge.fury.io/js/async-caf.svg)](https://www.npmjs.org/package/async-caf)

@@ -24,3 +24,3 @@ [![Dependencies](https://david-dm.org/getify/caf.svg)](https://david-dm.org/getify/caf)

// function that when called, returns a promise.
var main = CAF( function *main(cancelToken,url){
var main = CAF( function *main(signal,url){
var resp = yield ajax( url );

@@ -35,9 +35,9 @@

// returned promise
main( token, "http://some.tld/other" )
main( token.signal, "http://some.tld/other" )
.then( onResponse, onCancelOrError );
// only wait 3 seconds for the request!
// only wait 5 seconds for the request!
setTimeout( function(){
token.cancel( "Request took too long!" );
}, 3000 );
token.abort( "Request took too long!" );
}, 5000 );
```

@@ -47,3 +47,3 @@

Moreover, the generator itself is provided the cancelation token (`cancelToken` parameter above), so you can call another `function*` generator with **CAF**, and pass along the shared cancelation token. In this way, a single cancelation signal cascades across however many `function*` generators are currently in the execution chain:
Moreover, the generator itself is provided the cancelation token's signal (`signal` parameter above), so you can call another `function*` generator with **CAF**, and pass along the shared cancelation token signal. In this way, a single cancelation signal cascades across however many `function*` generators are currently in the execution chain:

@@ -53,27 +53,27 @@ ```js

var one = CAF( function *one(cancelToken,v){
return yield two(cancelToken,v);
var one = CAF( function *one(signal,v){
return yield two(signal,v);
} );
var two = CAF( function *two(cancelToken,v){
return yield three(cancelToken,v);
var two = CAF( function *two(signal,v){
return yield three(signal,v);
} );
var three = CAF( function* three(cancelToken,v){
var three = CAF( function* three(signal,v){
return yield ajax( `http://some.tld/?v=${v}` );
} );
one( token, 42 );
one( token.signal, 42 );
// cancel request if not completed in 5 seconds
setTimeout(function(){
token.cancel();
// only wait 5 seconds for the request!
setTimeout( function(){
token.abort( "Request took too long!" );
}, 5000 );
```
In this snippet, `one(..)` calls and waits on `two(..)`, `two(..)` calls and waits on `three(..)`, and `three(..)` calls and waits on `ajax(..)`. Because the same cancelation token is used for the 3 generators, if `token.cancel()` is executed while they're all still paused, they will all immediately abort.
In this snippet, `one(..)` calls and waits on `two(..)`, `two(..)` calls and waits on `three(..)`, and `three(..)` calls and waits on `ajax(..)`. Because the same cancelation token is used for the 3 generators, if `token.abort()` is executed while they're all still paused, they will all immediately abort.
**Note:** In this example, the cancelation token has no effect on the `ajax(..)` call, since that utility ostensibly doesn't provide cancelation capability. The Ajax request itself would still run to its completion (or error or whatever), but we've canceled the `one(..)`, `two(..)`, and `three(..)` functions that were waiting to process its response.
**Note:** In this example, the cancelation token has no effect on the actual `ajax(..)` call itself, since that utility ostensibly doesn't provide cancelation capability; the Ajax request itself would still run to its completion (or error or whatever). We've only canceled the `one(..)`, `two(..)`, and `three(..)` functions that were waiting to process its response. See [`AbortController(..)`](#abortcontroller) and [Manual Cancelation Signal Handling](#manual-cancelation-signal-handling) below for addressing this concern.
## Overview
## Background/Motivation

@@ -92,2 +92,4 @@ An `async function` and a `function*` generator (driven with a [generator-runner](https://github.com/getify/You-Dont-Know-JS/blob/master/async%20%26%20performance/ch4.md#promise-aware-generator-runner)) look, generally speaking, very similar. For that reason, most people just prefer `async function` since it's a little nicer syntax and doesn't require a library to provide the runner.

## Overview
These two functions are essentially equivalent; `one(..)` is an actual `async function`, whereas `two(..)` will behave like an async function in that it also returns a promise:

@@ -115,7 +117,7 @@

two( token, 21 )
two( token.signal, 21 )
.then( console.log, console.error ); // 42
```
If `token.cancel(..)` is executed while `two(..)` is still running, its promise will be rejected. If you pass a cancelation reason (any value, but typically a string) to `token.cancel(..)`, that will be passed as the promise rejection:
If `token.abort(..)` is executed while `two(..)` is still running, the signal's promise will be rejected. If you pass a cancelation reason (any value, but typically a string) to `token.abort(..)`, that will be the promise rejection reason:

@@ -126,7 +128,109 @@ ```js

setTimeout( function(){
token.cancel( "Took too long!" );
}, 10 );
token.abort( "Took too long!" );
```
### `finally { .. }`
Canceling a **CAF**-wrapped `function*` generator that is paused does cause it to abort right away, but if there's a pending `finally {..}` clause, that will still have a chance to run.
Moreover, a `return` of a non-`undefined` value in that pending `finally {..}` clause will override the completion result of the function:
```js
var token = new CAF.cancelToken();
var main = CAF( function *main(signal,url){
try {
return yield ajax( url );
}
finally {
return 42;
}
} );
main( token.signal, "http://some.tld/other" )
.catch( console.log ); // 42 <-- not "Aborting!"
token.abort( "Aborting!" );
```
Whatever value is passed to `abort(..)`, if any, is normally the completion value (promise rejection reason) of the function. But in this case, `42` overrides the `"Aborting!"` value.
### `AbortController(..)`
`CAF.cancelToken(..)` extends [`AbortController`, the DOM standard](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) for canceling/aborting operations like `fetch(..)` calls. As such, a cancelation token's signal can be passed directly to a DOM API like `fetch(..)` and it will respond to it accordingly:
```js
var token = new CAF.cancelToken();
var main = CAF(function *main(signal,url) {
var resp = await fetch( url, { signal } );
console.log( resp );
return resp;
});
main( token.signal, "http://some.tld/other" )
.catch( console.log ); // "Aborting!"
token.abort( "Aborting!" );
```
**Note:** If the standard `AbortController` is not defined in the environment, it's [polyfilled](https://github.com/mo/abortcontroller-polyfill).
### Manual Cancelation Signal Handling
Even if you aren't calling a cancelation signal-aware utility (like `fetch(..)`), you can still manually listen to the cancelation signal:
```js
var token = new CAF.cancelToken();
var main = CAF( function *main(signal,url){
// listen to the signal's promise rejection directly
signal.pr.catch( reason => {
// reason == "Aborting!"
} );
var resp = yield ajax( url );
console.log( resp );
return resp;
} );
main( token.signal, "http://some.tld/other" )
.catch( console.log ); // "Aborting!"
token.abort( "Aborting!" );
```
**Note:** The `catch(..)` handler inside of `main(..)` will still run, even though `main(..)` will be aborted at its waiting `yield` statement. If there was a way to manually cancel the `ajax(..)` call, that code could run here.
And even if you aren't running in a **CAF**-wrapped function, you can still respond to the cancelation signal manually to interrupt flow control:
```js
var token = new CAF.cancelToken();
// normal async function
async function main(signal,url) {
try {
var resp = await Promise.race( [
ajax( url ),
signal.pr
] );
console.log( resp );
return resp;
}
catch (err) {
// err == "Aborting!"
}
}
main( token.signal, "http://some.tld/other" )
.catch( console.log ); // "Aborting!"
token.abort( "Aborting!" );
```
**Note:** As discussed earlier, the `ajax(..)` call itself is not cancelation aware, and is thus not being aborted here. But we *are* aborting our waiting on the `ajax(..)` call. When `signal.pr` wins the `Promise.race(..)` race and creates an exception from its promise rejection, flow control jumps to the `catch (err) { .. }` clause.
## npm Package

@@ -148,3 +252,3 @@

[![Build Status](https://travis-ci.org/getify/caf.svg?branch=master)](https://travis-ci.org/getify/caf)
[![Build Status](https://travis-ci.org/getify/CAF.svg?branch=master)](https://travis-ci.org/getify/CAF)
[![npm Module](https://badge.fury.io/js/async-caf.svg)](https://www.npmjs.org/package/async-caf)

@@ -151,0 +255,0 @@

@@ -13,12 +13,14 @@ #!/usr/bin/env node

SRC_DIR = path.join(ROOT_DIR,"src"),
LIB_DIR = path.join(ROOT_DIR,"lib"),
DIST_DIR = path.join(ROOT_DIR,"dist"),
LIB_SRC = path.join(SRC_DIR,"caf.src.js"),
LIB_DIST = path.join(DIST_DIR,"caf.js"),
POLYFILL_SRC = path.join(LIB_DIR,"abortcontroller-polyfill-modified.js"),
CORE_SRC = path.join(SRC_DIR,"caf.src.js"),
CORE_DIST = path.join(DIST_DIR,"caf.js"),
result
result = ""
;
console.log("*** Building Core ***");
console.log(`Building: ${LIB_DIST}`);
console.log(`Building: ${CORE_DIST}`);

@@ -32,4 +34,6 @@ try {

result += fs.readFileSync(POLYFILL_SRC,{ encoding: "utf8" });
result += "\n" + fs.readFileSync(CORE_SRC,{ encoding: "utf8" });
// NOTE: since uglify doesn't yet support ES6, no minifying happening :(
result = fs.readFileSync(LIB_SRC,{ encoding: "utf8" });

@@ -68,3 +72,3 @@ // result = ugly.minify(path.join(SRC_DIR,"caf.src.js"),{

// write dist
fs.writeFileSync( LIB_DIST, result /* result.code + "\n" */, { encoding: "utf8" } );
fs.writeFileSync( CORE_DIST, result /* result.code + "\n" */, { encoding: "utf8" } );

@@ -71,0 +75,0 @@ console.log("Complete.");

@@ -14,2 +14,3 @@ #!/usr/bin/env node

else {
require(path.join("..","lib","abortcontroller-polyfill-modified.js"));
global.CAF = require(path.join("..","src","caf.src.js"));

@@ -16,0 +17,0 @@ }

(function UMD(name,context,definition){
/* istanbul ignore next */if (typeof define === "function" && define.amd) { define(definition); }
/* istanbul ignore next */else if (typeof module !== "undefined" && module.exports) { module.exports = definition(); }
/* istanbul ignore next */else if (typeof module !== "undefined" && module.exports) { module.exports = definition(name,context); }
/* istanbul ignore next */else { context[name] = definition(name,context); }

@@ -8,4 +8,21 @@ })("CAF",this,function DEF(name,context){

cancelToken.prototype.cancel = cancel;
cancelToken.prototype.listen = listen;
class cancelSignal extends AbortSignal {
constructor() {
super();
this.pr = new Promise((_,rej)=>this.rej = rej);
this.pr.catch(_=>1); // silence unhandled rejection warnings
}
}
class cancelToken extends AbortController {
constructor() {
super();
this.signal = new cancelSignal();
}
abort(reason) {
super.abort();
this.signal.rej(reason);
}
}
CAF.cancelToken = cancelToken;

@@ -21,13 +38,11 @@

return function instance(cancelToken,...args){
var trigger;
var canceled = new Promise(function c(_,rej){
trigger = rej;
});
var { it, pr } = _runner.call(this,generatorFn,cancelToken,...args);
cancelToken.listen(function onCancel(reason){
try { var ret = it.return(); } catch (err) {}
trigger(ret.value !== undefined ? ret.value : reason);
it = pr = trigger = null;
var cancel = cancelToken.pr.catch(function onCancel(reason){
try {
var ret = it.return();
throw ret.value !== undefined ? ret.value : reason;
}
finally { it = pr = cancel = null; }
});
var race = Promise.race([ pr, canceled ]);
var race = Promise.race([ pr, cancel ]);
race.catch(_=>1); // silence unhandled rejection warnings

@@ -38,27 +53,2 @@ return race;

function cancelToken() {
this.canceled = false;
this.cancelationReason = undefined;
this.listeners = [];
}
function cancel(reason) {
this.cancelationReason = reason;
this.canceled = true;
// note: running in LIFO instead of FIFO order
// to ensure that cascaded cancelations run in
// expected order
while (this.listeners.length > 0) {
let cb = this.listeners.pop();
try { cb(reason); } catch (err) {}
}
}
function listen(cb) {
if (this.canceled) {
try { cb(this.cancelationReason); } catch (err) {}
}
else {
this.listeners.push(cb);
}
}
// thanks to Benjamin Gruenbaum (@benjamingr on GitHub) for

@@ -65,0 +55,0 @@ // big improvements here!

"use strict";
QUnit.test( "API", function test(assert){
assert.expect( 5 );
assert.expect( 6 );

@@ -9,7 +9,8 @@ assert.ok( _isFunction( CAF ), "CAF()" );

assert.ok( _isFunction( CAF.cancelToken ), "CAF.cancelToken()" );
assert.ok( _isFunction( (new CAF.cancelToken()).listen ), "CAF.cancelToken#listen()" );
assert.ok( _isFunction( (new CAF.cancelToken()).cancel ), "CAF.cancelToken#cancel()" );
assert.ok( _isObject( (new CAF.cancelToken()).signal ), "CAF.cancelToken#signal" );
assert.ok( _isObject( (new CAF.cancelToken()).signal.pr ), "CAF.cancelToken#signal.pr" );
assert.ok( _isFunction( (new CAF.cancelToken()).abort ), "CAF.cancelToken#abort()" );
} );
QUnit.test( "cancelToken", function test(assert){
QUnit.test( "cancelToken.abort()", async function test(assert){
function checkParameter(reason) {

@@ -24,15 +25,13 @@ assert.step(reason);

"quit",
"---",
"quit",
];
token.listen(checkParameter);
token.listen(checkParameter);
token.cancel("quit");
assert.step("---");
token.listen(checkParameter);
token.signal.pr.catch(checkParameter);
token.abort("quit");
token.signal.pr.catch(checkParameter);
await token.signal.pr.catch(_=>1);
// rActual;
assert.expect( 5 ); // note: 1 assertion + 4 `step(..)` calls
assert.expect( 3 ); // note: 1 assertion + 2 `step(..)` calls
assert.verifySteps( rExpected, "cancelation reason passed" );

@@ -44,3 +43,3 @@ } );

assert.step(this.x);
assert.step(String(cancelToken === token));
assert.step(String(cancelToken === token.signal));
assert.step(a);

@@ -68,3 +67,3 @@ assert.step(b);

// rActual;
var pActual = asyncFn.call(obj,token,"3","12");
var pActual = asyncFn.call(obj,token.signal,"3","12");
var qActual = await pActual;

@@ -100,3 +99,3 @@ pActual = pActual.toString();

setTimeout(function(){
token.cancel("Quit!");
token.abort("Quit!");
},50);

@@ -106,3 +105,3 @@

try {
await main(token,20);
await main(token.signal,20);
}

@@ -143,3 +142,3 @@ catch (err) {

setTimeout(function(){
token.cancel();
token.abort();
},50);

@@ -149,3 +148,3 @@

try {
await main(token,20);
await main(token.signal,20);
}

@@ -197,4 +196,4 @@ catch (err) {

"secondary: 3",
"main: done",
"secondary: done",
"main: done",
];

@@ -207,3 +206,3 @@ var pExpected = "Quit!";

setTimeout(function(){
token.cancel("Quit!");
token.abort("Quit!");
},50);

@@ -213,3 +212,3 @@

try {
await main(token,20);
await main(token.signal,20);
}

@@ -216,0 +215,0 @@ catch (err) {

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc