task-serializer
Advanced tools
Comparing version 1.0.4 to 1.1.0
'use strict'; | ||
const {createPreprocStream}=require('mini-preproc'); | ||
const fs=require('fs'); | ||
async function main(){ | ||
let outdir='./usage-examples'; | ||
async function oneSet(nodeJSonly){ | ||
let outdir=nodeJSonly? | ||
'./usage-examples-nodejs-only':'./usage-examples'; | ||
fs.mkdirSync(outdir,{recursive:true}); | ||
@@ -18,3 +19,7 @@ let proms=[]; | ||
.on('error',reject) | ||
.pipe(createPreprocStream({RELEASE:true},{strip:true})) | ||
.pipe(createPreprocStream({ | ||
RELEASE:true, | ||
NODEJS:nodeJSonly | ||
}, | ||
{strip:true})) | ||
.on('error',reject) | ||
@@ -28,4 +33,8 @@ .pipe(fs.createWriteStream(outdir+'/'+fn)) | ||
} | ||
async function main(){ | ||
await Promise.all([oneSet(false),oneSet(true)]); | ||
} | ||
//oneSet(false) | ||
main() | ||
.then(()=>{console.log("success"); process.exitCode=0;}) | ||
.catch((e)=>{console.log("failure: "+e.message); process.exitCode=1;}); |
@@ -1,2 +0,1 @@ | ||
/* eslint-disable no-constant-condition */ | ||
'use strict'; | ||
@@ -6,7 +5,10 @@ //--IF{{RELEASE}} | ||
//--ELSE | ||
/* eslint-disable no-constant-condition */ | ||
const {AsyncIter}=require('./uif-async-iter.js'); | ||
//--ENDIF | ||
//--STOP | ||
//--IF{{NODEJS}} | ||
const {exitOnBeforeExit,producer}=require('./demo-lib.js'); | ||
//--ELSE | ||
//--const {producer}=require('./demo-lib.js'); | ||
//--ENDIF | ||
async function consumer(ai){ | ||
@@ -28,2 +30,3 @@ do{ | ||
} | ||
//--IF{{NODEJS}} | ||
main() | ||
@@ -33,1 +36,6 @@ .then(()=>{console.log('success');process.exitCode=0;}) | ||
exitOnBeforeExit(2); | ||
//--ELSE | ||
//--main() | ||
//-- .then(()=>{console.log('success');}) | ||
//-- .catch((e)=>{console.log('failure '+e.message);}); | ||
//--ENDIF |
@@ -7,5 +7,7 @@ 'use strict'; | ||
//--ENDIF | ||
//--STOP | ||
//--IF{{NODEJS}} | ||
const {exitOnBeforeExit,producer}=require('./demo-lib.js'); | ||
//--ELSE | ||
//--const {producer}=require('./demo-lib.js'); | ||
//--ENDIF | ||
async function consumer(ts){ | ||
@@ -33,6 +35,11 @@ await new Promise((resolve)=>{ | ||
} | ||
//--IF{{NODEJS}} | ||
main() | ||
.then(()=>{console.log('success');process.exitCode=0;}) | ||
.catch((e)=>{console.log('failure '+e.message);process.exitCode=1;}); | ||
exitOnBeforeExit(2); | ||
//--ELSE | ||
//--main() | ||
//-- .then(()=>{console.log('success');}) | ||
//-- .catch((e)=>{console.log('failure '+e.message);}); | ||
//--ENDIF |
@@ -0,4 +1,32 @@ | ||
//--IF{{RELEASE}} | ||
//--ELSE | ||
/* eslint-disable indent */ | ||
//--ENDIF | ||
'use strict'; | ||
//--IF{{RELEASE}} | ||
//--var {AsyncIter,NextSymbol}=require('task-serializer'); | ||
//--ELSE | ||
var {AsyncIter,NextSymbol}=require('./index'); | ||
//--ENDIF | ||
function snooze(ms){return new Promise(r=>setTimeout(r,ms));} | ||
function range(len){return [...Array(len).keys()];} | ||
function makepr(){ | ||
let pr={}; | ||
pr.promise=new Promise((r)=>{pr.resolve=r;}); | ||
return pr; | ||
} | ||
function logStatus(ts){ | ||
let wa=ts.getCountWaiting(); | ||
let wo=ts.getCountWorking(); | ||
let rest=ts.getCountResolvedTotal(); | ||
let rejt=ts.getCountRejectedTotal(); | ||
let fint=ts.getCountFinishedTotal(); | ||
console.log( | ||
`wa:${wa},wo:${wo},rest:${rest},rejt:${rejt},fint:${fint}`); | ||
if ((ts instanceof AsyncIter)||(ts instanceof NextSymbol)){ | ||
let resnr=ts.getCountResolvedNotRead(); | ||
let rejnr=ts.getCountRejectedNotRead(); | ||
console.log(`resnr:${resnr},rejnr:${rejnr}`); | ||
} | ||
} | ||
async function task(id,ms,err=false){ | ||
@@ -12,21 +40,15 @@ console.log(`-->enter ${id}`); | ||
} | ||
function exitOnBeforeExit(exitCode){ | ||
process.on('beforeExit',async()=>{ | ||
if (typeof process.exitCode=='undefined'){ | ||
console.error('unexpected "beforeExit" event'); | ||
process.exit(exitCode); | ||
} else | ||
process.exit(process.exitCode); | ||
}); | ||
} | ||
function makepr(){ | ||
let pr={}; | ||
pr.promise=new Promise((r)=>{pr.resolve=r;}); | ||
return pr; | ||
} | ||
async function producer(ts){ | ||
for (let i=0; i<6; i++){ | ||
//--IF{{RELEASE}} | ||
//--ELSE | ||
logStatus(ts); | ||
//--ENDIF | ||
ts.addTask(task,i,2**(10-i),(i+1)%3==0); | ||
await snooze(100); | ||
} | ||
//--IF{{RELEASE}} | ||
//--ELSE | ||
logStatus(ts); | ||
//--ENDIF | ||
ts.addEnd(); | ||
@@ -39,3 +61,15 @@ console.log('producer finished'); | ||
module.exports.makepr=makepr; | ||
module.exports.producer=producer; | ||
//--IF{{NODEJS}} | ||
function exitOnBeforeExit(exitCode){ | ||
process.on('beforeExit',async()=>{ | ||
if (typeof process.exitCode=='undefined'){ | ||
console.error('unexpected "beforeExit" event'); | ||
process.exit(exitCode); | ||
} else | ||
process.exit(process.exitCode); | ||
}); | ||
} | ||
module.exports.exitOnBeforeExit=exitOnBeforeExit; | ||
module.exports.producer=producer; | ||
//--ENDIF |
@@ -7,5 +7,7 @@ 'use strict'; | ||
//--ENDIF | ||
//--STOP | ||
const {makepr,exitOnBeforeExit,producer}=require('./demo-lib.js'); | ||
//--IF{{NODEJS}} | ||
const {exitOnBeforeExit,makepr,producer}=require('./demo-lib.js'); | ||
//--ELSE | ||
//--const {makepr,producer}=require('./demo-lib.js'); | ||
//--ENDIF | ||
var somethingElse=makepr(); | ||
@@ -34,4 +36,4 @@ var iv=setInterval(()=>{somethingElse.resolve("somethingElse");},300); | ||
break;} | ||
case ts.symbolEmpty():{ | ||
console.log("symbolEmpty"); | ||
case ts.symbolAllRead():{ | ||
console.log("symbolAllRead"); | ||
emptied=true; | ||
@@ -47,6 +49,11 @@ clearInterval(iv); | ||
} | ||
//--IF{{NODEJS}} | ||
main() | ||
.then(()=>{console.log('success');process.exitCode=0;}) | ||
.catch((e)=>{console.log('failure: '+e.message);process.exitCode=1;}) | ||
; | ||
.catch((e)=>{console.log('failure '+e.message);process.exitCode=1;}); | ||
exitOnBeforeExit(2); | ||
//--ELSE | ||
//--main() | ||
//-- .then(()=>{console.log('success');}) | ||
//-- .catch((e)=>{console.log('failure '+e.message);}); | ||
//--ENDIF |
@@ -7,5 +7,7 @@ 'use strict'; | ||
//--ENDIF | ||
//--STOP | ||
//--IF{{NODEJS}} | ||
const {exitOnBeforeExit,producer}=require('./demo-lib.js'); | ||
//--ELSE | ||
//--const {producer}=require('./demo-lib.js'); | ||
//--ENDIF | ||
async function consumer_waitAll(ts){ | ||
@@ -38,2 +40,3 @@ try{ | ||
} | ||
//--IF{{NODEJS}} | ||
main() | ||
@@ -43,1 +46,6 @@ .then(()=>{console.log('success');process.exitCode=0;}) | ||
exitOnBeforeExit(2); | ||
//--ELSE | ||
//--main() | ||
//-- .then(()=>{console.log('success');}) | ||
//-- .catch((e)=>{console.log('failure '+e.message);}); | ||
//--ENDIF |
{ | ||
"name": "task-serializer", | ||
"version": "1.0.4", | ||
"version": "1.1.0", | ||
"description": "Serialize tasks/promises for integrated control. Option for limiting number of concurrent tasks.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
142
README.md
@@ -19,2 +19,4 @@ copyright 2020 craigphicks ISC license | ||
The module is not dependent upon NodeJS, so can be used in browser code. | ||
# Usage examples | ||
@@ -35,3 +37,3 @@ | ||
const {AsyncIter}=require('task-serializer'); | ||
const {exitOnBeforeExit,producer}=require('./demo-lib.js'); | ||
const {producer}=require('./demo-lib.js'); | ||
async function consumer(ai){ | ||
@@ -54,4 +56,4 @@ do{ | ||
main() | ||
.then(()=>{console.log('success');process.exitCode=0;}) | ||
.catch((e)=>{console.log('failure '+e.message);process.exitCode=1;}); | ||
.then(()=>{console.log('success');}) | ||
.catch((e)=>{console.log('failure '+e.message);}); | ||
``` | ||
@@ -64,4 +66,3 @@ | ||
const {NextSymbol}=require('task-serializer'); | ||
const {makepr,exitOnBeforeExit,producer}=require('./demo-lib.js'); | ||
const {makepr,producer}=require('./demo-lib.js'); | ||
var somethingElse=makepr(); | ||
@@ -90,4 +91,4 @@ var iv=setInterval(()=>{somethingElse.resolve("somethingElse");},300); | ||
break;} | ||
case ts.symbolEmpty():{ | ||
console.log("symbolEmpty"); | ||
case ts.symbolAllRead():{ | ||
console.log("symbolAllRead"); | ||
emptied=true; | ||
@@ -104,5 +105,4 @@ clearInterval(iv); | ||
main() | ||
.then(()=>{console.log('success');process.exitCode=0;}) | ||
.catch((e)=>{console.log('failure: '+e.message);process.exitCode=1;}) | ||
; | ||
.then(()=>{console.log('success');}) | ||
.catch((e)=>{console.log('failure: '+e.message);}); | ||
``` | ||
@@ -115,3 +115,3 @@ | ||
const {WaitAll}=require('task-serializer'); | ||
const {exitOnBeforeExit,producer}=require('./demo-lib.js'); | ||
const {producer}=require('./demo-lib.js'); | ||
async function consumer_waitAll(ts){ | ||
@@ -145,4 +145,4 @@ try{ | ||
main() | ||
.then(()=>{console.log('success');process.exitCode=0;}) | ||
.catch((e)=>{console.log('failure '+e.message);process.exitCode=1;}); | ||
.then(()=>{console.log('success');}) | ||
.catch((e)=>{console.log('failure '+e.message);}); | ||
``` | ||
@@ -155,3 +155,3 @@ | ||
const {Callbacks}=require('task-serializer'); | ||
const {exitOnBeforeExit,producer}=require('./demo-lib.js'); | ||
const {producer}=require('./demo-lib.js'); | ||
async function consumer(ts){ | ||
@@ -180,5 +180,4 @@ await new Promise((resolve)=>{ | ||
main() | ||
.then(()=>{console.log('success');process.exitCode=0;}) | ||
.catch((e)=>{console.log('failure '+e.message);process.exitCode=1;}); | ||
exitOnBeforeExit(2); | ||
.then(()=>{console.log('success');}) | ||
.catch((e)=>{console.log('failure '+e.message);}); | ||
``` | ||
@@ -188,13 +187,6 @@ | ||
```js | ||
'use strict'; | ||
var {AsyncIter,NextSymbol}=require('task-serializer'); | ||
function snooze(ms){return new Promise(r=>setTimeout(r,ms));} | ||
function range(len){return [...Array(len).keys()];} | ||
function exitOnBeforeExit(exitCode){ | ||
process.on('beforeExit',async()=>{ | ||
if (typeof process.exitCode=='undefined'){ | ||
console.error('unexpected "beforeExit" event'); | ||
process.exit(exitCode); | ||
} else | ||
process.exit(process.exitCode); | ||
}); | ||
} | ||
function makepr(){ | ||
@@ -205,2 +197,16 @@ let pr={}; | ||
} | ||
function logStatus(ts){ | ||
let wa=ts.getCountWaiting(); | ||
let wo=ts.getCountWorking(); | ||
let rest=ts.getCountResolvedTotal(); | ||
let rejt=ts.getCountRejectedTotal(); | ||
let fint=ts.getCountFinishedTotal(); | ||
console.log( | ||
`wa:${wa},wo:${wo},rest:${rest},rejt:${rejt},fint:${fint}`); | ||
if ((ts instanceof AsyncIter)||(ts instanceof NextSymbol)){ | ||
let resnr=ts.getCountResolvedNotRead(); | ||
let rejnr=ts.getCountRejectedNotRead(); | ||
console.log(`resnr:${resnr},rejnr:${rejnr}`); | ||
} | ||
} | ||
async function task(id,ms,err=false){ | ||
@@ -222,3 +228,9 @@ console.log(`-->enter ${id}`); | ||
} | ||
module.exports.snooze=snooze; | ||
module.exports.task=task; | ||
module.exports.range=range; | ||
module.exports.makepr=makepr; | ||
module.exports.producer=producer; | ||
``` | ||
# Esential Information | ||
@@ -231,4 +243,3 @@ | ||
## Class have differing output functions and behavior | ||
## Classes have differing output functions and behavior | ||
The output interface of each of those classes differ, and are suitable for different usage cases. The following table compares some properties of those classes to help decide which is suitable for a given usage case: | ||
@@ -242,3 +253,5 @@ | ||
| select style | no | yes | N/A | N/A | | ||
|||||| | ||
where 'property' are as follows: | ||
@@ -279,5 +292,6 @@ - 'read buffered': | ||
- *read* | ||
- Task/promise outcome has been read by the consumer. This state might not be reached of reading is abandoned, e.g. due to a rejected-value. | ||
- This milestone is seperate from *finished* only in the read-buffered classes `AsyncIter`,`NextSymbol`, and `WaitAll`. In the case of class `Callbacks`, one of the `onTaskResolved`/`onTaskRejected` callbacks is called immediately when *finished* is reached, so *read* and *finished* are reached simultaneously. | ||
- Task/promise outcome has been read by the consumer. | ||
- The class instance passed through the following milestones, in order: | ||
- The class instance passes through the following milestones, in order: | ||
- *started-processing* | ||
@@ -287,7 +301,26 @@ - First task/promise has been *added*. | ||
- `addEnd` has been called to guarantee no more tasks/promises will be added. | ||
- *finished-processing* | ||
- `addEnd` has been called and all *added* tasks have reached *finished*. | ||
- *empty* | ||
- `addEnd` has been called and all *added* tasks have reached *read*. | ||
- *all-finished* | ||
- `addEnd` has been called and all *added* tasks/promises have reached *finished*. | ||
- *all-read* | ||
- This milestone is seperate from *all-finished* only for the read-buffered classes `AsyncIter`, `NextSymbol`, and `WaitAll`. In the case of `Callbacks`, one of the callbacks `onResolved`/`onRejected` will be called immediately upon reaching *finished*. | ||
## Termination | ||
There is no active termination method, but there is passive termination when `instance` is no longer referenced, or processing is deliberately abandoned. | ||
The following two cases are important: | ||
- *termination-after-all-finished* | ||
- In this case the only possible references in the class state are to a buffer of read values. The class will successfully be garbage collected. | ||
- *termination-before-all-finished* | ||
- In this case at least one task/promise has not resolved or rejected. The class is not guaranteed to garbage collect in this case. However, if the JS engine is able to determine that for each task/promise not resolved or rejected, that task/promise is deadlocked(/*), then the class might be garbage collected. | ||
(/*) 'Deadlocked' means a task/promise is waiting on a promise, but there is no possibility that the waited-on-promise can be resolved. The prototypical example of such a promise is `new Promise(()=>{})`. More typically interconnected promises deadlock in a practical example of the [Dining Philosopher's Problem](https://en.wikipedia.org/wiki/Dining_philosophers_problem). Note that in JS, this will NOT happen to a promise directly or indirectly waiting upon external input, e.g., when waiting upon `process.stdin` while `process.stdin` is active. | ||
In case of running under nodeJS, There is another important termination case: | ||
- *termination-due-to-unexpected-deadlock* | ||
- In the case of nodeJS, when the internal nodeJS event queue becomes empty, nodeJS may decide that the async function referencing our class `instance` is deadlocked, and cause that async function to return. This can be very confusing. However, in the author's experience, the deadlock is always real - i.e., it is due to programmer error. | ||
- The nodeJS function `process.onBeforeExit()`, can be helpful in diagnosing unexpected deadlock. It sets a callback which will be called when nodeJS diagnoses the whole program as being "in deadlock". Example code using `process.onBeforeExit()` can found under the node module `node_modules/task-serializer/usage-examples-nodejs-only/demo-lib.js`. | ||
# APIs | ||
@@ -307,4 +340,26 @@ | ||
- this a guarantee from the caller that `addTask` will not be called again. It is required so the instance knows that when all tasks/promises have reached the *finished* milestone, the instance has reached the *finished-processing* milestone. | ||
- Generally, when possible, *rejected-values* are passed to the consumer before *resolved-values*. `WaitAll.waitAllSettled()` is one exception to that rule. | ||
The following are informational functions common to all the classes. | ||
- `instance.getCountWaiting()` | ||
- When the construct arg `concurrentTaskLimit<=0`, always returns `0`. | ||
- Otherwise, returns the number of tasks *added* but not yet *started*. | ||
- `instance.getCountWorking()` | ||
- Returns the number of tasks/promises *started* but not yet *finished*. | ||
- `instance.getCountResolvedTotal()` | ||
- Returns the number of tasks/promises *finished* with a *resolved-value* | ||
- `instance.getCountRejectedTotal()` | ||
- Returns the number of tasks/promises *finished* with a *rejected-value* | ||
- `instance.getCountFinishedTotal()` | ||
- Returns the sum of `instance.getCountResolvedTotal()` and `instance.getCountRejectedTotal()` | ||
The following are informational functions available only in the read-buffered classes `AsyncIter` and `NextSymbol`: | ||
- `instance.getCountResolvedNotRead()` | ||
- Returns the number of tasks/promises *finished* with a *resolved-value*, but which are not yet *read* | ||
- `instance.getCountRejectedNotRead()` | ||
- Returns the number of tasks/promises *finished* with a *rejected-value*, but which are not yet *read* | ||
- `instance.getCountFinishedNotRead()` | ||
- Returns the sum of `instance.getCountResolvedNotRead()` and `instance.getCountRejectedNotRead()` | ||
## `AsyncIter` only API | ||
@@ -316,3 +371,3 @@ - see [`AsyncIter` usage example](#asynciter-usage-example) for example. | ||
- explicit async `instance.next()` or implicit async `for await (iter of instance)` | ||
- There are 3 possible outcome categories: *resolved-value*, *rejected-value*, and *instance-empty*, where *instance-empty* indicated the instance has reached the *empty* milestone. | ||
- There are 3 possible outcome categories: *resolved-value*, *rejected-value*, and *all-read*, where *all-read* indicates that the instance has reached the *all-read* milestone. | ||
- case: explicit | ||
@@ -323,3 +378,3 @@ - case: *resolved-value* | ||
- throws `<rejected-value>` | ||
- case: *instance-empty* | ||
- case: *all-read* | ||
- returns `{done:true}` | ||
@@ -331,3 +386,3 @@ - case: implicit | ||
- throws `<rejected-value>` | ||
- case: *instance-empty* | ||
- case: *all-read* | ||
- breaks from loop. | ||
@@ -341,6 +396,6 @@ | ||
- async `instance.nextSymbol()` | ||
Returns a value strictly equal to one of `instance.symbolTaskResolved()`, `instance.symbolTaskRejected()`, or `instance.symbolEnd()`. | ||
Returns a value strictly equal to one of `instance.symbolTaskResolved()`, `instance.symbolTaskRejected()`, or `instance.symbolAllRead()`. | ||
- case `instance.symbolTaskResolved()`: indicates a task/promise *resolved-value* is ready to be read. | ||
- case `instance.symbolTaskRejected()`: indicates a task/promise *rejected-value* is ready to be read. | ||
- case `instance.symbolTaskResolved()`: indicates the instance milestone *empty* has been reached. | ||
- case `instance.symbolAllRead()`: indicates the instance milestone *all-read* has been reached. | ||
- `instance.getTaskResolvedValue()` | ||
@@ -382,9 +437,2 @@ - This is a sync function intended to be called immediately after `async instance.nextSymbol()` has returned a value equal to `instance.symbolTaskResolved()` | ||
# TODO: | ||
- Add in informational functions | ||
- `instance.getCountAddedNotStarted()` | ||
- `instance.getCountStarterNotFinished()` | ||
- `instance.getCountFinishedNotRead()` | ||
- `instance.getCountRead()` | ||
@@ -32,6 +32,9 @@ 'use strict'; | ||
this._usingConcurrentLimit=(concurrentLimit>0); | ||
this._sem=new Semaphore( | ||
this._usingConcurrentLimit?concurrentLimit:Number.MAX_SAFE_INTEGER); | ||
if (this._usingConcurrentLimit) | ||
this._sem=new Semaphore(concurrentLimit); | ||
this._numAdded=0; | ||
this._numFinished=0; | ||
//this._numFinished=0; | ||
// each finished with be either resolved or rejected | ||
this._numResolved=0; | ||
this._numRejected=0; | ||
this._onEmptyCallback=null; | ||
@@ -47,3 +50,4 @@ this._onTaskResolvedCallback=null; | ||
let p=(async()=>{ | ||
await this._sem.wait(); | ||
if (this._usingConcurrentLimit) | ||
await this._sem.wait(); | ||
try { | ||
@@ -59,2 +63,3 @@ let result; | ||
} | ||
this._numResolved++; | ||
if (this._onTaskResolvedCallback) | ||
@@ -64,2 +69,3 @@ this._onTaskResolvedCallback(result); | ||
} catch(e) { | ||
this._numRejected++; | ||
if (this._onTaskRejectedCallback) | ||
@@ -69,4 +75,4 @@ this._onTaskRejectedCallback(e); | ||
} finally { | ||
this._sem.signal(); | ||
this._numFinished++; | ||
if (this._usingConcurrentLimit) | ||
this._sem.signal(); | ||
if (this._endFlag | ||
@@ -95,5 +101,13 @@ && this.getWaitingCount()==0 && this.getWorkingCount()==0){ | ||
} | ||
getWorkingCount(){return this._numAdded-this._sem.getWaitingCount()-this._numFinished;} | ||
getWaitingCount(){return this._sem.getWaitingCount();} | ||
getFinishedCount(){return this._numFinished;} | ||
getWorkingCount(){ | ||
return this._numAdded-this.getFinishedCount()-this.getWaitingCount(); | ||
} | ||
getWaitingCount(){ | ||
return this._usingConcurrentLimit?this._sem.getWaitingCount():0; | ||
} | ||
getFinishedCount(){ | ||
return this.getResolvedCount()+this.getRejectedCount(); | ||
} | ||
getResolvedCount(){return this._numResolved;} | ||
getRejectedCount(){return this._numRejected;} | ||
onEmpty(callback){this._onEmptyCallback=callback;} | ||
@@ -100,0 +114,0 @@ onTaskResolved(callback){this._onTaskResolvedCallback=callback;} |
@@ -60,2 +60,12 @@ 'use strict'; | ||
} | ||
// informationals | ||
getCountWaiting(){return this._sts.getWaitingCount();} | ||
getCountWorking(){return this._sts.getWorkingCount();} | ||
getCountResolvedNotRead(){return this._q.length;} | ||
getCountRejectedNotRead(){return this._qe.length;} | ||
getCountFinishedNotRead(){return this._q.length+this._qe.length;} | ||
// the following are monotonically increasing totals, | ||
getCountResolvedTotal(){return this._sts.getResolvedCount();} | ||
getCountRejectedTotal(){return this._sts.getRejectedCount();} | ||
getCountFinishedTotal(){return this._sts.getFinishedCount();} | ||
} | ||
@@ -62,0 +72,0 @@ |
@@ -14,6 +14,11 @@ /* eslint-disable no-unused-vars */ | ||
addEnd(){this._ts.addEnd();} | ||
getWorkingCount(){return this._ts.getWorkingCount();} | ||
getWaitingCount(){return this._ts.getWaitingCount();} | ||
getFinishedCount(){return this._ts.getFinishedCount();} | ||
// informationals | ||
getCountWaiting(){return this._ts.getWaitingCount();} | ||
getCountWorking(){return this._ts.getWorkingCount();} | ||
// Callbacks has no read buffering, | ||
// so the following are monotonically increasing totals | ||
getCountResolvedTotal(){return this._ts.getResolvedCount();} | ||
getCountRejectedTotal(){return this._ts.getRejectedCount();} | ||
getCountFinishedTotal(){return this._ts.getFinishedCount();} | ||
} | ||
module.exports.Callbacks=Callbacks; |
@@ -8,5 +8,3 @@ const {TaskSerializer}=require('./task-serializer.js'); | ||
this._result=TaskSerializer._makepr(); | ||
this._result.flag=false; | ||
this._error=TaskSerializer._makepr(); | ||
this._error.flag=false; | ||
this._qresults=[]; | ||
@@ -17,3 +15,2 @@ this._qerrors=[]; | ||
this._qresults.push(result); | ||
this._result.flag=true; | ||
this._result.resolve(); | ||
@@ -23,3 +20,2 @@ }); | ||
this._qerrors.push(error); | ||
this._error.flag=true; | ||
this._error.resolve(); | ||
@@ -32,14 +28,16 @@ }); | ||
this._symTaskRejected=Symbol('TaskRejected'); | ||
this._symEmpty=Symbol('empty'); | ||
this._symAllRead=Symbol('AllRead'); | ||
} | ||
getTaskResolvedValue(){ | ||
if (!this._result.flag) throw new Error('getTaskResolvedValue - not ready'); | ||
this._result=TaskSerializer._makepr(); | ||
this._result.flag=false; | ||
if (!this._qresults.length) | ||
throw new Error('getTaskResolvedValue - not ready'); | ||
if (this._qresults.length==1) | ||
this._result=TaskSerializer._makepr(); | ||
return this._qresults.splice(0,1)[0]; | ||
} | ||
getTaskRejectedValue(){ | ||
if (!this._error.flag) throw new Error('getTaskRejectedValue - not ready'); | ||
this._error=TaskSerializer._makepr(); | ||
this._error.flag=false; | ||
if (!this._qerrors.length) | ||
throw new Error('getTaskRejectedValue - not ready'); | ||
if (this._qerrors.length==1) | ||
this._error=TaskSerializer._makepr(); | ||
return this._qerrors.splice(0,1)[0]; | ||
@@ -53,25 +51,27 @@ } | ||
} | ||
// isSymbolEmpty(sym){return sym===this._symEmpty;} | ||
// isSymbolTaskResolved(sym){return sym===this._symTaskResolved;} | ||
// isSymbolTaskRejected(sym){return sym===this._symTaskRejected;} | ||
symbolEmpty(){return this._symEmpty;} | ||
symbolAllRead(){return this._symAllRead;} | ||
symbolTaskResolved(){return this._symTaskResolved;} | ||
symbolTaskRejected(){return this._symTaskRejected;} | ||
nextSymbol(){// this promise can be safely abandoned | ||
// Note: the order of promises ensures that this._symAllRead | ||
// won't be returned until all task results are actually read. | ||
return Promise.race([ | ||
this._error.promise.then(()=>{return this._symTaskRejected;}), | ||
this._result.promise.then(()=>{return this._symTaskResolved;}), | ||
this._empty.promise.then(()=>{return this._symEmpty;}), | ||
this._empty.promise.then(()=>{return this._symAllRead;}), | ||
]); | ||
} | ||
// async promiseNextValue(){ // this promise canNOT be safely abandoned | ||
// let sym=await this.promiseNextSymbol(); | ||
// if (this.isSymbolTaskRejected(sym)) | ||
// throw this.waitTaskRejected(); | ||
// else if (this.isSymbolTaskResolved(sym)) | ||
// return this.waitTaskResolved(); | ||
// else if (this.isSymbolEmpty(sym)) | ||
// return sym; | ||
// } | ||
// informationals | ||
getCountWaiting(){return this._ts.getWaitingCount();} | ||
getCountWorking(){return this._ts.getWorkingCount();} | ||
getCountResolvedNotRead(){return this._qresults.length;} | ||
getCountRejectedNotRead(){return this._qerrors.length;} | ||
getCountFinishedNotRead(){ | ||
return this._qresults.length+this._qerrors.length; | ||
} | ||
// the following are monotonically increasing totals, | ||
getCountResolvedTotal(){return this._ts.getResolvedCount();} | ||
getCountRejectedTotal(){return this._ts.getRejectedCount();} | ||
getCountFinishedTotal(){return this._ts.getFinishedCount();} | ||
} | ||
module.exports.NextSymbol=NextSymbol; |
@@ -37,3 +37,11 @@ const {TaskSerializer}=require('./task-serializer.js'); | ||
} | ||
// informationals | ||
getCountWaiting(){return this._ts.getWaitingCount();} | ||
getCountWorking(){return this._ts.getWorkingCount();} | ||
// the following are monotonically increasing totals, | ||
getCountResolvedTotal(){return this._ts.getResolvedCount();} | ||
getCountRejectedTotal(){return this._ts.getRejectedCount();} | ||
getCountFinishedTotal(){return this._ts.getFinishedCount();} | ||
} | ||
module.exports.WaitAll=WaitAll; |
@@ -1,6 +0,4 @@ | ||
/* eslint-disable no-constant-condition */ | ||
'use strict'; | ||
const {AsyncIter}=require('task-serializer'); | ||
const {exitOnBeforeExit,producer}=require('./demo-lib.js'); | ||
const {producer}=require('./demo-lib.js'); | ||
async function consumer(ai){ | ||
@@ -23,4 +21,3 @@ do{ | ||
main() | ||
.then(()=>{console.log('success');process.exitCode=0;}) | ||
.catch((e)=>{console.log('failure '+e.message);process.exitCode=1;}); | ||
exitOnBeforeExit(2); | ||
.then(()=>{console.log('success');}) | ||
.catch((e)=>{console.log('failure '+e.message);}); |
'use strict'; | ||
const {Callbacks}=require('task-serializer'); | ||
const {exitOnBeforeExit,producer}=require('./demo-lib.js'); | ||
const {producer}=require('./demo-lib.js'); | ||
async function consumer(ts){ | ||
@@ -28,5 +27,3 @@ await new Promise((resolve)=>{ | ||
main() | ||
.then(()=>{console.log('success');process.exitCode=0;}) | ||
.catch((e)=>{console.log('failure '+e.message);process.exitCode=1;}); | ||
exitOnBeforeExit(2); | ||
.then(()=>{console.log('success');}) | ||
.catch((e)=>{console.log('failure '+e.message);}); |
@@ -0,4 +1,24 @@ | ||
'use strict'; | ||
var {AsyncIter,NextSymbol}=require('task-serializer'); | ||
function snooze(ms){return new Promise(r=>setTimeout(r,ms));} | ||
function range(len){return [...Array(len).keys()];} | ||
function makepr(){ | ||
let pr={}; | ||
pr.promise=new Promise((r)=>{pr.resolve=r;}); | ||
return pr; | ||
} | ||
function logStatus(ts){ | ||
let wa=ts.getCountWaiting(); | ||
let wo=ts.getCountWorking(); | ||
let rest=ts.getCountResolvedTotal(); | ||
let rejt=ts.getCountRejectedTotal(); | ||
let fint=ts.getCountFinishedTotal(); | ||
console.log( | ||
`wa:${wa},wo:${wo},rest:${rest},rejt:${rejt},fint:${fint}`); | ||
if ((ts instanceof AsyncIter)||(ts instanceof NextSymbol)){ | ||
let resnr=ts.getCountResolvedNotRead(); | ||
let rejnr=ts.getCountRejectedNotRead(); | ||
console.log(`resnr:${resnr},rejnr:${rejnr}`); | ||
} | ||
} | ||
async function task(id,ms,err=false){ | ||
@@ -12,16 +32,2 @@ console.log(`-->enter ${id}`); | ||
} | ||
function exitOnBeforeExit(exitCode){ | ||
process.on('beforeExit',async()=>{ | ||
if (typeof process.exitCode=='undefined'){ | ||
console.error('unexpected "beforeExit" event'); | ||
process.exit(exitCode); | ||
} else | ||
process.exit(process.exitCode); | ||
}); | ||
} | ||
function makepr(){ | ||
let pr={}; | ||
pr.promise=new Promise((r)=>{pr.resolve=r;}); | ||
return pr; | ||
} | ||
async function producer(ts){ | ||
@@ -39,3 +45,3 @@ for (let i=0; i<6; i++){ | ||
module.exports.makepr=makepr; | ||
module.exports.exitOnBeforeExit=exitOnBeforeExit; | ||
module.exports.producer=producer; | ||
'use strict'; | ||
const {NextSymbol}=require('task-serializer'); | ||
const {makepr,exitOnBeforeExit,producer}=require('./demo-lib.js'); | ||
const {makepr,producer}=require('./demo-lib.js'); | ||
var somethingElse=makepr(); | ||
@@ -28,4 +27,4 @@ var iv=setInterval(()=>{somethingElse.resolve("somethingElse");},300); | ||
break;} | ||
case ts.symbolEmpty():{ | ||
console.log("symbolEmpty"); | ||
case ts.symbolAllRead():{ | ||
console.log("symbolAllRead"); | ||
emptied=true; | ||
@@ -42,5 +41,3 @@ clearInterval(iv); | ||
main() | ||
.then(()=>{console.log('success');process.exitCode=0;}) | ||
.catch((e)=>{console.log('failure: '+e.message);process.exitCode=1;}) | ||
; | ||
exitOnBeforeExit(2); | ||
.then(()=>{console.log('success');}) | ||
.catch((e)=>{console.log('failure '+e.message);}); |
'use strict'; | ||
const {WaitAll}=require('task-serializer'); | ||
const {exitOnBeforeExit,producer}=require('./demo-lib.js'); | ||
const {producer}=require('./demo-lib.js'); | ||
async function consumer_waitAll(ts){ | ||
@@ -33,4 +32,3 @@ try{ | ||
main() | ||
.then(()=>{console.log('success');process.exitCode=0;}) | ||
.catch((e)=>{console.log('failure '+e.message);process.exitCode=1;}); | ||
exitOnBeforeExit(2); | ||
.then(()=>{console.log('success');}) | ||
.catch((e)=>{console.log('failure '+e.message);}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1241
416
2
57688