Asynchronous Chilkat Methods in Node.js

Chilkat implements a thread pool for asynchronous tasks in all programming languages except for Node.js. For Node.js, Chilkat tasks run directly on Node’s internal thread pool (i.e. the libuv thread pool). The Task.Run() method can have 0 or 1 arguments. If an argument is passed, it is the “task completed” callback that get called when the task has completed. Here is an example showing how to send email asynchronously using Chilkat in Node.js

var os = require('os');
if (os.platform() == 'win32') {  
    var chilkat = require('chilkat_win32'); 
} else if (os.platform() == 'linux') {
    if (os.arch() == 'x86') {
        var chilkat = require('chilkat_linux32');
    } else {
        var chilkat = require('chilkat_linux64');
    }
} else if (os.platform() == 'darwin') {
	var chilkat = require('chilkat_macosx');
}

// While a task is running, there are three standard callbacks that can be made (if desired).
// They are progressInfo, percentDone, and abortCheck.   These callbacks are made directly
// from the libuv worker thread.

// progressInfo callbacks have name/value string arguments.
// Experiment to see what information is provided for any given task.
function progressInfo(name, value) {
    console.log(name + ": " + value);
}

// The percentDone callback provides an integer percentage completion value, typically from 0 to 100.
// percentDone callbacks are made only in cases where it is possible to know a percentage completed.
// For example, establishing a socket or TLS connection will not have percentDone callbacks, because
// it makes no sense - a client simply waits until the server accepts the connection.  It's never in a state
// where it can be "50% done".
function percentDone(pctDone) {
    console.log("percent complete: " + pctDone);
    // Return true to cause the task to abort.
    // Return false to allow the task to continue.
    return false;	
}

// The abortCheck event is called periodically to allow the application to abort an asynchronous task.
function abortCheck() {
    console.log("abort check");
    // Return true to cause the task to abort.
    // Return false to allow the task to continue.
    return false;
}


// The taskCompleted event is called from the main Node.js thread.
function taskCompleted(task) {

    // Before looking at the task result, close the connection with the SMTP server..
    success = mailman.CloseSmtpConnection();
    if (success != true) {
        console.log("Connection to SMTP server not closed cleanly.");
    }

    //  A finished/completed task may be one that was canceled, aborted, or truly finished.
    //  If the task was "canceled", it was canceled prior to actually starting.  

    //  If the task "completed", then it ran to completion, but the actual success/failure of the method
    //  is determined by the result obtained via one of the GetResult* methods.  (A "completed" task will
    //  have a StatusInt equal to 7.   If the task finished, but was not completed, then it must've
    //  been aborted or canceled:
    if (task.StatusInt != 7) {
        console.log("Task did not complete.");
        console.log("task status: " + task.Status);
        return;
    }

    //  The SendEmail method returns a boolean.  Therefore, after the task is finished,
    //  we can get the boolean result by calling GetResultBool.  This is the return value had
    //  SendEmail been called synchronously.
    success = task.GetResultBool();
    if (success != true) {
        //  The task's ResultErrorText contains what would have been in the LastErrorText property had
        //  the SendEmail method been called synchronously.
        console.log(task.ResultErrorText);
    }
    else {
        console.log("Email sent asynchronously.");
    }
}


function chilkatExample() {

    //  All Chilkat classes can be unlocked at once at the beginning of a program
    //  by calling UnlockBundle.  It requires a Bundle unlock code.
    var chilkatGlob = new chilkat.Global();
    var success = chilkatGlob.UnlockBundle("Anything for 30-day trial.");
    if (success != true) {
        console.log(chilkatGlob.LastErrorText);
        return;
    }

    var mailman = new chilkat.MailMan();

    //  Set the SMTP server and any required settings.
    mailman.SmtpHost = "smtp.mymailserver.com";
    mailman.SmtpUsername = "myLogin";
    mailman.SmtpPassword = "myPassword";
    mailman.StartTLS = true;

    //  Create a new email object
    var email = new chilkat.Email();

    email.Subject = "This is a test";
    email.Body = "This is a test";
    email.From = "Chilkat Support <support@chilkatsoft.com>";
    success = email.AddTo("Chilkat Admin","admin@chilkatsoft.com");

    //  Call the async version of the SendEmail method to return a task object.
    //  The task object is loaded, but is in the Inert state -- meaning it is
    //  not yet scheduled to run on Node's libuv thread pool.
    var task = mailman.SendEmailAsync(email);
    if (task == null ) {
        console.log(mailman.LastErrorText);
        return;
    }

    // Hookup callbacks that are called while the task is running.
    // Notice that these are on the mailman object, not on the Task object.
    mailman.ProgressInfo = progressInfo;
    mailman.PercentDone = percentDone;
    mailman.AbortCheck = abortCheck;

    //  Schedule the task for running on Node's thread pool.  This changes the task's state
    //  from Inert to Live.
    // Pass the taskCompleted function so that it runs asynchronously.
    // If no arguments are passed to task.Run(), then it runs synchronously (following Node's conventions).
    success = task.Run(taskCompleted);
    if (success != true) {
        console.log(task.LastErrorText);
        return;
    }

    //  The application continues while the email is being sent in one of Node's worker threads.
}

chilkatExample();


Event Callbacks in Java

Event callbacks in Java (including Android) are supported starting in v9.5.0.52. To receive event callbacks, first create a Java class derived from one of the Chilkat event callback classes. The event callback classes are: CkBaseProgress, CkHttpProgress, CkZipProgress, CkFtp2Progress, CkMailManProgress, CkTarProgress, and CkSFtpProgress.

All future Chilkat classes will only use CkBaseProgress. Theses event callback classes will be documented in the “Events” section of each class’s online reference documentation.

Here is an example of an HTTP download using event callbacks in Java. First we have the MyHttpProgress derived from CkHttpProgress. Following that, we have the simple Java program that installs the event handler and does the HTTP download. Event callbacks will also happen with the asynchronous version of the methods, but beware of the fact that when asynchronous, the task is running in a background thread and the callback occurs in the background thread.

import com.chilkatsoft.CkHttpProgress;
import com.chilkatsoft.CkTask;


public class MyHttpProgress extends CkHttpProgress 
{	
  public boolean AbortCheck()
  	{
      System.out.println("AbortCheck");
      // Return true to abort, false to allow the method to continue.
      return false;
  	}
  	
  // pctDone is a value from 0 to 100
  // (it is actually value from 0 to the PercentDoneScale property setting)
  public boolean PercentDone(int pctDone)
  {
    System.out.println(pctDone);
    // Return true to abort, false to allow the method to continue.
    // Note: A PercentDone event is the equivalent of an AbortCheck.  
    // When PercentDone events are frequently firing, AbortCheck events are suppressed.
    // AbortCheck events will fire when the time between PercentDone events is longer 
    // than the HeartbeatMs property setting.
    return false;
  }
  
  public void ProgressInfo(String name, String value)
  {
    System.out.println(name + ": " + value);
  }
  
  public void TaskCompleted(CkTask task)
  {
     System.out.println("task completed!");
  }
  
  }

The synchronous Java program w/ event callbacks… (the asynchronous version follows..)

import com.chilkatsoft.CkHttp;

public class HttpWithEvents {
	
  static {
    try {
    	
    	System.loadLibrary("chilkat");
    	
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load.\n" + e);
      System.exit(1);
    }
  }


	
  public static void main(String argv[])
  	{
    CkHttp http = new CkHttp();
    
    // Install an event callback handler to get progress events.
    MyHttpProgress myProgress = new MyHttpProgress();
    http.put_EventCallbackObject(myProgress);
    
    boolean success;

    //  Any string unlocks the component for the 1st 30-days.
    success = http.UnlockComponent("Anything for 30-day trial");
    if (success != true) {
        System.out.println(http.lastErrorText());
        return;
    }

    //  Download a file at a URL.
    success = http.Download("http://www.chilkatsoft.com/download/9.5.0.51/ChilkatDotNet45-9.5.0-x64.zip","ChilkatDotNet45-9.5.0-x64.zip");
    if (success != true) {
        System.out.println(http.lastErrorText());
        return;
    }
    
    System.out.println("OK");
  	}
  }

The asynchronous Java program w/ event callbacks…

import com.chilkatsoft.CkHttp;
import com.chilkatsoft.CkTask;

public class AsyncHttp {
	
  static {
    try {
    	
    	System.loadLibrary("chilkat");
    	
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load.\n" + e);
      System.exit(1);
    }
  }


	
  public static void main(String argv[])
  	{
    CkHttp http = new CkHttp();
    
    // Install an event callback handler to get progress events.
    MyHttpProgress myProgress = new MyHttpProgress();
    http.put_EventCallbackObject(myProgress);
    
    boolean success;

    //  Any string unlocks the component for the 1st 30-days.
    success = http.UnlockComponent("Anything for 30-day trial");
    if (success != true) {
        System.out.println(http.lastErrorText());
        return;
    }

    //  Download a file at a URL.
    CkTask task = http.DownloadAsync("http://www.chilkatsoft.com/download/9.5.0.51/ChilkatDotNet45-9.5.0-x64.zip","ChilkatDotNet45-9.5.0-x64.zip");
    if (success != true) {
        System.out.println(http.lastErrorText());
        return;
    }
    
    task.put_UserData("chilkatDotNet45");
    
    if (!task.Run()) {
        System.out.println(task.lastErrorText());
        return;
	}
  
    System.out.println("OK, task is running...");
    
    // Wait a max of 10 seconds for it to finish.
    success = task.Wait(10000);
    
    // What is the task status?
    System.out.println("task status = " + task.status());
  	}
  }

VB.NET TaskCompleted Event for Asynchronous Method Call

Demonstrates a TaskCompleted event for an asynchronous Chilkat method call.
The event callback occurs in the background thread, and therefore any updates to the UI must
happen on the UI thread. For this reason, MethodInvoker is used to make updates to a TextBox..

    Dim WithEvents http As New Chilkat.Http()

    Private Sub http_OnTaskCompleted(sender As Object, args As Chilkat.TaskCompletedEventArgs) Handles http.OnTaskCompleted

        Dim task As Chilkat.Task = args.Task

        ' This event callback is running in the background thread.
        ' To update a UI element, we must be on the UI thread..
        Me.Invoke(New MethodInvoker(
           Sub()
               ' The task.UserData can be used to identify the particular asynchronous method call
               ' that this callback belongs to.
               If (task.UserData = "chilkatHomePage") Then
                   ' An asychronous method is simply calling the corresponding synchronous method
                   ' in a background thread.  In this case, it is a call to QuickGetStr,
                   ' which returned a string, or it returned Nothing for failure.  This result is available
                   ' via task.GetResultString.  The LastErrorText for the background call 
                   ' is available in task.ResultErrorText
                   Dim htmlPage As String = task.GetResultString()
                   If (htmlPage Is Nothing) Then
                       TextBox1.Text = task.ResultErrorText
                   Else
                       TextBox1.Text = htmlPage
                   End If
               End If
           End Sub))
    End Sub

    Private Sub AsyncToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles AsyncToolStripMenuItem.Click

        http = New Chilkat.Http()

        Dim task As Chilkat.Task = http.QuickGetStrAsync("http://www.chilkatsoft.com/")
        If (task Is Nothing) Then
            TextBox1.Text = http.LastErrorText
            Exit Sub
        End If

        ' We can set the task.UserData property to identify this particular asynchronous call in the callback.
        task.UserData = "chilkatHomePage"

        ' Start the task in a background thread
        Dim success As Boolean = task.Run()
        If (Not success) Then
            TextBox1.Text = task.LastErrorText
            Exit Sub
        End If

    End Sub

Delphi TaskCompleted Event (using the Chilkat ActiveX)

Demonstrates the TaskCompleted event callback using the Chilkat Delphi ActiveX:

procedure TForm1.httpProgressInfo(ASender: TObject;  const name: WideString; const value: WideString);

begin
    // This event callback occurs in the background thread (because the asynchronous
    // version of the QuickGetStr method was called (i.e. QuickGetStrAsync)
    // UI elements must be updated from the main UI thread...
    TThread.Synchronize(nil, procedure begin
      Memo1.Lines.Add(name + ': ' + value);
      end);

end;

procedure TForm1.httpTaskCompleted(ASender: TObject;  const task: IChilkatTask);
begin
    // This event callback occurs in the background thread (because the asynchronous
    // version of the QuickGetStr method was called (i.e. QuickGetStrAsync)
    // UI elements must be updated from the main UI thread...
    TThread.Synchronize(nil, procedure begin
      Memo1.Lines.Add(task.ResultErrorText);
      end);

end;

procedure TForm1.Button2Click(Sender: TObject);
var
  http: TChilkatHttp;
  success: Integer;
  html: WideString;
  task: IChilkatTask;

begin
  http := TChilkatHttp.Create(Self);

  success := http.UnlockComponent('anything for 30-day trial');
  if (success <> 1) then
  begin
    Memo1.Lines.Add(http.LastErrorText);
    Exit;
  end;

  http.OnTaskCompleted := httpTaskCompleted;
  http.OnProgressInfo := httpProgressInfo;

  // Create a task to do the HTTP GET asynchronously.
  task := http.QuickGetStrAsync('http://www.chilkatsoft.com/');

  // Start the task in a background thread.
  task.Run();

  // Warning: If an event callback method calls TThread.Synchronize, then a call to task.Wait will hang
  // for the full duration of the timeout specified.  (If the wait-forever value of 0 is passed
  // to task.Wait, then it will hang forever.) This is because the call to TThread.Synchronize is waiting
  // for the main thread's event loop.  Control is not returned to the main event loop until this
  // TForm1.Button2Click returns -- thus there is a deadlock. There is no deadlock when a program
  // Waits on a task and there are no event callbacks that call TTHread.Synchronize.
  //task.Wait(5000);

end;

C# TaskCompleted Event — Updating the UI from a Background Thread

This sample C# snippet demonstrates how to use the TaskCompleted event to be notified when an asynchronous method completes. Given that the event callback is in the background thread, any UI updates must occur on the main thread. This example demonstrates how to do it:

	private void taskCompletedToolStripMenuItem_Click(object sender, EventArgs e)
	    {
	    // Demonstrate an asynchronous Chilkat method call with a TaskCompleted event.
	    Chilkat.Http http = new Chilkat.Http();

	    http.OnTaskCompleted += http_OnTaskCompleted;

	    Chilkat.Task task = http.QuickGetStrAsync("http://www.chilkatsoft.com/");

	    // We can set the task.UserData property to identify this particular asynchronous call in the callback.
	    task.UserData = "chilkatHomePage";

	    // Runs the HTTP GET asynchronously on a background thread..
	    task.Run();
	    }

	// This event fires in the background thread. 
	// 
	void http_OnTaskCompleted(object sender, Chilkat.TaskCompletedEventArgs args)
	    {
	    Chilkat.Task task = args.Task;

	    // This event callback is running in the background thread.
	    // To update a UI element, we must be on the UI thread..
	    this.Invoke((MethodInvoker)delegate
		{
		    // The task.UserData can be used to identify the particular asynchronous method call
		    // that this callback belongs to.
		    if (task.UserData.Equals("chilkatHomePage"))
			{
			// An asychronous method is simply calling the corresponding synchronous method
			// in a background thread.  In this case, it is a call to QuickGetStr,
			// which returned a string, or it returned Nothing for failure.  This result is available
			// via task.GetResultString.  The LastErrorText for the background call 
			// is available in task.ResultErrorText
			string htmlPage = task.GetResultString();
			if (htmlPage == null)
			    {
			    textBox2.Text = task.ResultErrorText;
			    }
			else
			    {
			    textBox2.Text = htmlPage;
			    }
			}
		});
	    }
	}

Chilkat v9.5.0.52 – Asynchronous for all Classes in all Programming Languages

Starting in Chilkat v9.5.0.52, asynchronous capability is added across all classes and all programming languages. The new Task and TaskChain classes have been added.

To Beta test, send email to support@chilkatsoft.com. Please specify the programming language, operating system, etc. that is needed.

The online reference documentation has been updated. Examples will be forthcoming. Version 9.5.0.52 will be released later this month.

The existing async functionality in Http, Ftp2, Socket, and Upload will become deprecated and will eventually be removed. Deprecated functionality will be removed in a future 9.6.0 release, where the ActiveX will have new CLSID’s, and a new CreateObject name (with 9_6_0) in the name so as to be able to coexist with the 9_5_0_* ActiveX’s. This will not happen for quite some time (months from now or early 2016).