
Security News
Deno 2.6 + Socket: Supply Chain Defense In Your CLI
Deno 2.6 introduces deno audit with a new --socket flag that plugs directly into Socket to bring supply chain security checks into the Deno CLI.
Yet another promise library, but this one is designed intentionally against the ES6 promise pattern, which asynchronously resolves promise callbacks in the next tick of the JS engine. In many cases, asynchronous resolution is the safest and easiest-to-understand implementation of a promise, but it adds a huge delay to the resolution, which in most places is unnecessary. Moreover, when we attempted to wrap the IndexedDB architecture with standard ES6 promises, it falls apart, because IndexedDB closes database connections when control is passed back to the main thread. We started building NoSQLProvider and immediately ran into this problem. SyncTasks is the solution to that problem, but is also a performant answer to asynchronous programming problems in general. In addition, we've worked in a simple optional cancellation mechanism that chains through promise resolution as well (as long as you chain through SyncTasks promises, and don't mix in non-cancellation-supported promises.)
Usage of SyncTasks promises is somewhat similar to JQuery promises. If you need to create a promise deferral, you call
SyncTasks.Defer() and it returns a SyncTasks.Deferred object. The usual flow is to stash the deferral away for your async
logic to later resolve/reject, and call .promise() on the deferral and return that to your caller, so that the caller only
works with the promise (for chaining and resolution-callback reasons). Any calls of resolve/reject are synchronously resolved
before returning from the resolve/reject method.
SyncTasks has the basic Defer call, but also helper methods to save on common tasks for interacting with promises/deferrals:
Defer() - Returns a new blank SyncTasks.Deferral.Resolved(obj?) - Returns a new SyncTasks.Promise, already in a resolved (success) state, with the optional passed
resolution value.Rejected(obj?) - Returns a new SyncTasks.Promise, already in a rejected (failure) state, with the optional passed
resolution value.all([promises...]) - Returns a new SyncTasks.Promise that will resolve when all of the promises in the list have finished.
If any of the promises resolved with failure, then all will resolve with failure, with the first non-undefined resolution
value as the resolution value for all. If all are successful, then it will resolve successfully, with the resolution value
being an array of the resolution values from the input promises. You can also pass values instead of promises in the input
list, and those values will pass through as successful resolutions to the output list.race([promises...]) - Returns a new SyncTasks.Promise that will resolve when any one of the promises in the list finish.
Resolves or rejects the output promise based on the resolution type and value of the first finished promise in the list.asyncCallback(callback) - Runs the specified callback function on the next tick of the javascript engine. Uses a shared
queue, so that all callbacks are played on the next tick sequentially, not one per tick. This is the same mechanism used
by thenAsync to queue the callbacks for the next tick, but is also a handy helper for more optimized defering of multiple
callbacks to the next tick.fromThenable(thenable) - A handy helper function to wrap any sort of Thenable (usually used for wrapping ES6 Promises) into
a SyncTask. This takes the thenable and maps its success and failure cases into a new SyncTasks.Promise that resolve and
reject with the results of the passed thenable.setTracingEnabled(boolean) - This option allows enabling of double resolution tracing individually per Promise.
Could be used in the release in cases then the problem couldn't be reproduced locally.
If option enabled assert will give you two stack traces - for the first resolve and the second. By default, you would see only second resolve stack trace.
Keep in mind that it adds an extra overhead as resolve method call will create an extra Error object, so it should be used with caution in the release.
Estimated overhead on mobile is around 0.05ms per resolve/reject call on Nexus 5x android.A created Deferral object only has 4 methods:
resolve(obj?) - Resolves any promises created by the deferral with success. Takes an optional value that is passed through the
success chain of any of the promises.reject(obj?) - Resolves any promises created by the deferral with failure. Takes an optional value that is passed through the
success chain of any of the promises.onCancel(callback) - Adds a cancellation callback function that is called whenever non-resolved/rejected promises created by the deferral get .cancel
called on them (or called on chained promises chain back up to this one.) This callback can be used to handle aborting async tasks that return promises.promise() - Returns a SyncTasks.Promise object from the deferral, which is then passed around the world.The Promise object is the public face of the async process that your Deferred is managing. You add various callbacks to
the promise object depending on what types of events you want to get notified about, and what type of chaining you want to
support. The methods supported are:
then(successCallback, failureCallback?) - The most common resolution mechanism for a promise. If the promise successfully
resolves, it will call the first callback (and will pass it the optional resolution value). If it resolves with failure,
the second callback is called (also with an optional resolution value). From inside each callback, you are able to return
either a value, which will then turn the resolution into a successful resolution and change the resolution value to the
value you just returned, or you may also return a new promise, which then continues the resolution chaining process until
something returns only a value (or undefined). Returning anything from either the successCallback or failureCallback
functions will chain to success of any subsequent promises. The then call returns a new promise which will be resolved
based on the resolution chain of the callbacks from this call.thenAsync(successCallback?, failureCallback?) - Has the same nuances and behavior as then, but the callbacks are called
asynchronously, in ES6 fashion, on the next tick of the javascript host engine.always(callback) - A synonym for calling then with the same callback for both parameters. Use this when you want to
always alter the resolution chain, regardless of whether it came in with success or failure. Just like then, returns
a new promise for chaining.catch(callback) - Has the same effect as calling then with the specified callback for the failureCallback parameter and
undefined for the successCallback -- this will only call your callback in the event of a chained failure case, but anything
returned from your callback here will chain future callbacks to success. Just like then, returns a new promise for
chaining.done(callback), fail(callback), and finally(callback) - If you would like to observe, but not change, the resolution
chain, you can use these functions. In all three cases, nothing you return from the callback functions will have any
effect, these are only for observing the resolution chain. The only difference between them is that done is only called
if the resolution chain is successful, fail is only called if the resolution chain is failure, and finally is called
in either case. The callback function is always passed the optional resolution value, but in finally's case, you have
no idea whether it was called based on success or failure. These three functions all return the same original promise
object, so you can attach multiple "observation functions" to the same promise without having to store it in a temporary
variable.cancel(obj?) - This method will notify the original deferral object of cancellation, and will pass it the optional value
it is called with, but has no further effects. If the deferral is not handling cancellation, then this call will do
absolutely nothing -- it does not guarantee any effects like failure resolution or any sort of stopping of chaining.
The cancellation attempt will walk backwards as far up the promise chain as possible, so if you cancel a promise, be
aware that it may end up calling cancel functions for deferrals many steps back in the promise chain.toEs6Promise() - A helper function that wraps SyncTasks.Promise object "back" into ES6 Promise. It directly maps success
and failure cases into respective calls to ES6 Promise constructor arguments resolve and rejectfunction sendMeAStringLater(numberOfMilliseconds: number, theString: string): SyncTasks.Promise<string> {
let defer = SyncTasks.Defer<string>();
setTimeout(() => {
defer.resolve(theString);
}, numberOfMilliseconds);
return defer.promise();
}
sendMeAStringLater(500, 'hi').then(myString => {
console.log(myString);
});
// 500 ms after running this, you will end up with a new console log line, "hi".
function sendMeAStringLater(numberOfMilliseconds: number, theString: string): SyncTasks.Promise<string> {
let defer = SyncTasks.Defer<string>();
let didFinish = false;
defer.onCancel(whyWasICancelled => {
if (!didFinish) {
didFinish = true;
defer.reject(whyWasICancelled);
}
});
setTimeout(() => {
// Make sure to bail here if it's already done. If you resolve a second time, it will throw an exception, since the
// cancel already resolved it once.
if (!didFinish) {
didFinish = true;
defer.resolve(theString);
}
}, numberOfMilliseconds);
return defer.promise();
}
let promise = sendMeAStringLater(500, 'hi').then(myString => {
console.log('Success: ' + myString);
}, errString => {
console.log('Failure: ' + errString);
});
setTimeout(() => {
promise.cancel('Sorry');
}, 200);
// 200 ms after running this, you will end up with a new console log line, "Failure: Sorry". The success case will not be
// run because it was already resolved with failure. If you change the 200ms timer to 600ms, then your console will change to
// "Success: hi" because the cancellation will happen after the success already did, so the `didFinish` check will swallow it.
FAQs
An explicitly non-A+ Promise library that resolves promises synchronously
We found that synctasks demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Deno 2.6 introduces deno audit with a new --socket flag that plugs directly into Socket to bring supply chain security checks into the Deno CLI.

Security News
New DoS and source code exposure bugs in React Server Components and Next.js: what’s affected and how to update safely.

Security News
Socket CEO Feross Aboukhadijeh joins Software Engineering Daily to discuss modern software supply chain attacks and rising AI-driven security risks.