Giter Club home page Giter Club logo

Comments (3)

scriby avatar scriby commented on July 23, 2024

You can call asyncblock.getCurrentFlow() to do what you want.

Thanks,

Chris

On Mar 17, 2016, at 3:07 AM, ani [email protected] wrote:

I'm trying to find a way to develop simple "blocking call" functions that perform multiple asynchronous function calls. This doesn't seem possible with the asyncblock module at the moment.

Asyncblock requires a function to create a new asyncblock scope any time the function needs to wait for one or more asynchronous functions to complete before returning. As per my initial understanding of asyncblock there's no way to wait on a flow.add() outside the asyncblock((flow) => {...}). Therefore a blocking function doesn't possible.

I never understood why creating nested asyncblock scope was necessary. I developed a demo using Fibers module to show that such nesting is not required. The blockingRoutine() below calls a simulated nonblockingRoutine(). The blockingRoutine() is quite clean, conceptually and literally. If we can retrieve the flow object without creating a new asyncblock scope then it's also possible to recreate a very simple blockingRoutine().

/*
2016-03-16

Objective:
Find a way to minimize changes needed to emulate blocking functions.

Motivation:
Synchronous programming is easier to develop and maintain. Co-routines
(fibers) help simplify synchronous programming under asynchronous
execution environments. Node.js is primarily a single threaded asynchronous
work loop design. Many APIs under Node.js are asynchronous. Simple
sequences of control calls in synchronous programming can turn into
complicated implementations under asynchronous programming. While several
solutions exist to make asynchronous sequential, the problem is minimizing
the maintenance cost required to use them.

State machines:
Need to add one state for every asynchronous function call and a set of
states to represent every asynchronous API. Incompatible with developing
blocking functions.

Lists:
Each function in a list of asynchronous functions is run once per
callback invoked. Additional framework support is needed for error
handling. Incompatible with developing blocking functions.

Promises:
Functions are wrapped in functors and chained together. Callbacks invoke
the next functor to execute. Error handling must also be added as a
functor in the sequence. Incompatible with developing blocking functions.

Generators:
Special built-in functors that maintain state. One asynchronous routine
called from a function forces all callers up the calling stack to
participate in the yield which means all callers must also turn into
generators. Incompatible with developing blocking functions.

Asyncblock:
Based on Fibers. All function calls within the asyncblock scope may be
sequential. Incompatible with developing blocking functions.

Fibers:
Implements co-routine primitives and supports nested fibers. Is possible
to build blocking functions, but how?

Strategy:
The solution will develop an Fiber wrapper to capture callback arguments
and will throw on errors.

Command-line:
node --harmony_destructuring test9.js
*/

"use strict";

//let optionForceError = true;
let optionForceError = false;

console.log("main start");
console.time("main finish");

let Fiber = require("fibers");

function getFiberCallback () {
let signal = Fiber.current;
return function () {
signal.run(arguments);
};
}

function waitForFiberToReturn (throwOnErrorFlag) {
if (typeof throwOnErrorFlag != "boolean" || throwOnErrorFlag) {
let [err, ...others] = Fiber.yield();
if (err) throw err;
return others;
}
return Fiber.yield();
}

function nonblockingRoutine (done) {
if (optionForceError)
setTimeout(() => done(new Error("something bad happened"), "test data"), 1000);
else
setTimeout(() => done(null, "test data"), 1000);
}

function blockingRoutine () {
console.log(" blockingTest 1");
nonblockingRoutine(getFiberCallback());
let result = waitForFiberToReturn();
console.log(" blockingTest 2");
return result;
}

Fiber(function () {
console.log(" outer 1");
try {
let result = blockingRoutine();
console.log(" result A = " + JSON.stringify(result));
} catch (e) {
console.log(" caught exception:", e.stack);
}
console.log(" outer 2");
}).run();

console.timeEnd("main finish");

/* OUTPUT (with optionForceError=false and waitForFiberToReturn() or waitForFiberToReturn(true)):

main start
outer 1
blockingTest 1
main finish: 6ms
blockingTest 2
result A = ["test data"]
outer 2
*/

/* OUTPUT (with optionForceError=false and waitForFiberToReturn(false)):

main start
outer 1
blockingTest 1
main finish: 6ms
blockingTest 2
result A = {"0":null,"1":"test data"}
outer 2
*/

/* OUTPUT (with optionForceError=true and waitForFiberToReturn() or waitForFiberToReturn(true)):

main start
outer 1
blockingTest 1
main finish: 6ms
caught exception: Error: something bad happened
at null._onTimeout (C:\Users\ayu\OneDrive\Project\fibers\test9.js:84:27)
at Timer.listOnTimeout (timers.js:92:15)
outer 2
*/

/* OUTPUT (with optionForceError=true and waitForFiberToReturn(false)):

main start
outer 1
blockingTest 1
main finish: 6ms
blockingTest 2
result A = {"0":{},"1":"test data"}
outer 2
*/

You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub

from asyncblock.

scriby avatar scriby commented on July 23, 2024

A couple more thoughts on this:

  • One drawback of using getCurrentFlow is that callers lose the ability to choose whether to execute a function as blocking or in parallel
  • Another drawback is that you can't use it with source transformation (func().sync()), as those have to be nested directly within an asyncblock for the parser to find them.
  • In a large application, it would be confusing if some functions auto-blocked and some required a callback. I think you'd want to use it consistently.
  • If libraries create their own "threads" by calling setTimeout or after I/O, those "threads" won't have a Fiber attached and the getCurrentFlow call will return null.

For those reasons, when writing code in general I don't use getCurrentFlow. However, in some special circumstances it can create a simpler API. For instance, I use it in https://github.com/scriby/browser-harness quite extensively, as it makes sense to auto-block when doing browser testing.

from asyncblock.

Animadei avatar Animadei commented on July 23, 2024

Can attest that all points are true and I'm OK with their properties in my applications.

In most cases the main work loop will be serial, each incoming message is handled by a switch, and each case calls a blocking function to process the message.

The blocking function can:

  1. call aync routines and parallel wait;
  2. create a nested asyncblock (no advantage over option 1);
  3. parallel wait on WebWorkers (similar to option 1).

The getCurrentFlow() is a core API in my opinion. It enables sequential programming by allowing signal/event primitive to be built and used. Co-routines are here to stay. It's just less overhead than Promises.

Thank you for your response and really appreciate your work.

from asyncblock.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.