使用libuv线程池实现Node.js异步函数

JavaScript是一种单线程的编程语言。在使用Node.js的时候,如果有耗时的操作,需要放到异步函数中。Node.js的底层使用了libuv,用于实现异步I/O。

学习资源

优化Node.js条形码插件

安装

同步接口

先看下同步接口是怎么实现的。

创建dbr.cc,并在里面增加一个函数DecodeFile

#include <node.h>
#include <node_buffer.h>
#include <string.h>
#include <uv.h>
#include "If_DBR.h"
#include "BarcodeFormat.h"
#include "BarcodeStructs.h"
#include "ErrorCode.h"
 
using namespace v8;
 
// Barcode format
const char * GetFormatStr(__int64 format)
{
    if (format == CODE_39)
        return "CODE_39";
    if (format == CODE_128)
        return "CODE_128";
    if (format == CODE_93)
        return "CODE_93";
    if (format == CODABAR)
        return "CODABAR";
    if (format == ITF)
        return "ITF";
    if (format == UPC_A)
        return "UPC_A";
    if (format == UPC_E)
        return "UPC_E";
    if (format == EAN_13)
        return "EAN_13";
    if (format == EAN_8)
        return "EAN_8";
    if (format == INDUSTRIAL_25)
        return "INDUSTRIAL_25";
    if (format == QR_CODE)
        return "QR_CODE";
    if (format == PDF417)
        return "PDF417";
    if (format == DATAMATRIX)
        return "DATAMATRIX";
 
    return "UNKNOWN";
}
 
/*
 *  decodeFile(fileName, barcodeTypes, callback)
 */
void DecodeFile(const FunctionCallbackInfo<Value>& args) {
 
    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope(isolate);
 
    // get arguments
    String::Utf8Value fileName(args[0]->ToString()); // convert v8 string to char *
    char *pFileName = *fileName; // file name
    __int64 llFormat = args[1]->IntegerValue();  // barcode types
    Local<Function> cb = Local<Function>::Cast(args[2]); // javascript callback function
 
    // initialize Dynamsoft Barcode Reader
    int iMaxCount = 0x7FFFFFFF;
    ReaderOptions ro = {0};
    pBarcodeResultArray pResults = NULL;
    ro.llBarcodeFormat = llFormat;
    ro.iMaxBarcodesNumPerPage = iMaxCount;
 
    // decode barcode image
    int ret = DBR_DecodeFile(pFileName, &ro, &pResults);
    if (ret)
        printf("Detection error code: %d\n", ret);
 
    int count = pResults->iBarcodeCount;
    pBarcodeResult* ppBarcodes = pResults->ppBarcodes;
    pBarcodeResult tmp = NULL;
 
    // array for storing barcode results
    Local<Array> barcodeResults = Array::New(isolate);
 
    for (int i = 0; i < count; i++)
    {
        tmp = ppBarcodes[i];
 
        Local<Object> result = Object::New(isolate);
        result->Set(String::NewFromUtf8(isolate, "format"), String::NewFromUtf8(isolate, GetFormatStr(tmp->llFormat)));
        result->Set(String::NewFromUtf8(isolate, "value"), String::NewFromUtf8(isolate, tmp->pBarcodeData));
        barcodeResults->Set(Number::New(isolate, i), result);
    }
 
    // release memory of barcode results
    DBR_FreeBarcodeResults(&pResults);
 
    // run the callback
    const unsigned argc = 1;
    Local<Value> argv[argc] = { barcodeResults };
    cb->Call(isolate->GetCurrentContext()->Global(), argc, argv);
}
 
void Init(Handle<Object> exports) {
    NODE_SET_METHOD(exports, "decodeFile", DecodeFile);
}
 
NODE_MODULE(dbr, Init)

现在可以使用JavaScript接口decodeFile了。创建binding.gyp。针对Windows, Linux和macOS添加不同的头文件和库路径:

{
  "targets": [
    {
      'target_name': "dbr",
      'sources': [ "dbr.cc" ],
      'conditions': [
          ['OS=="linux"', {
            'defines': [
              'LINUX_DBR',
            ],
            'include_dirs': [
                "/home/xiao/Dynamsoft/BarcodeReader4.0/Include"
            ],
            'libraries': [
                "-lDynamsoftBarcodeReaderx64", "-L/home/xiao/Dynamsoft/BarcodeReader4.0/Redist"
            ],
            'copies': [
            {
              'destination': 'build/Release/',
              'files': [
                '/home/xiao/Dynamsoft/BarcodeReader4.0/Redist/libDynamsoftBarcodeReaderx64.so'
              ]
            }]
          }],
          ['OS=="win"', {
            'defines': [
              'WINDOWS_DBR',
            ],
            'include_dirs': [
                "E:\Program Files (x86)\Dynamsoft\Barcode Reader 4.3\Components\C_C++\Include"
            ],
            'libraries': [
                "-lE:\Program Files (x86)\Dynamsoft\Barcode Reader 4.3\Components\C_C++\Lib\DBRx64.lib"
            ],
            'copies': [
            {
              'destination': 'build/Release/',
              'files': [
                'E:\Program Files (x86)\Dynamsoft\Barcode Reader 4.3\Components\C_C++\Redist\DynamsoftBarcodeReaderx64.dll'
              ]
            }]
          }],
          ['OS=="mac"', {
            'defines': [
              'MAC_DBR',
            ],
            'include_dirs' : [
                "/Applications/Dynamsoft/Barcode\ Reader\ 4.1/Include"
            ],
            'libraries': [
                "-lDynamsoftBarcodeReader"
            ]
          }]
      ]
    }
  ]
}

配置构建环境:

node-gyp configure

编译工程:

node-gyp build

创建dbr.js测试下:

var dbr = require('./build/Release/dbr');
var readline = require('readline');
var fs = require('fs');
var barcodeTypes = 0x3FF | 0x2000000 | 0x8000000 | 0x4000000; // 1D, QRCODE, PDF417, DataMatrix
 
function decodeFile(fileName) {
    dbr.decodeFile(
        fileName, barcodeTypes,
        function(msg) {
            var result = null;
            for (index in msg) {
                result = msg[index]
                console.log("Format: " + result['format']);
                console.log("Value : " + result['value']);
                console.log("##################");
            }
        }
    );
}
 
var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});
 
rl.question("Please input a barcode image path: ", function(answer) {
    decodeFile(answer);
    rl.close();
});

异步接口

虽然这样封装接口也是通过回调函数返回的,但是所有的工作都在主线程中,会造成堵塞。解决的方法就是把耗时的工作放到工作线程中。使用接口uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, uv_after_work_cb after_work_cb)可以把任务放到libuv的线程池中。

新建接口decodeFileAsync

/*
 *  decodeFileAsync(fileName, barcodeTypes, callback)
 */
void DecodeFileAsync(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope(isolate);
 
    // get arguments
    String::Utf8Value fileName(args[0]->ToString()); // file name
    char *pFileName = *fileName;
    __int64 llFormat = args[1]->IntegerValue(); // barcode types
    Local<Function> cb = Local<Function>::Cast(args[2]); // javascript callback function
 
    // initialize BarcodeWorker
    BarcodeWorker *worker = new BarcodeWorker;
    worker->request.data = worker;
    strcpy(worker->filename, pFileName);
    worker->callback.Reset(isolate, cb);
    worker->llFormat = llFormat;
    worker->pResults = NULL;
    worker->buffer = NULL;
     
    uv_queue_work(uv_default_loop(), &worker->request, (uv_work_cb)DetectionWorking, (uv_after_work_cb)DetectionDone);
}
 
void Init(Handle<Object> exports) {
    NODE_SET_METHOD(exports, "decodeFile", DecodeFile);
    NODE_SET_METHOD(exports, "decodeFileAsync", DecodeFileAsync);
}

把条形码的解码识别工作放到uv_work_cb回调函数中:

/*
 *  uv_work_cb
 */
static void DetectionWorking(uv_work_t *req)
{
    // get the reference to BarcodeWorker
    BarcodeWorker *worker = static_cast<BarcodeWorker *>(req->data);
 
    // initialize Dynamsoft Barcode Reader
    int iMaxCount = 0x7FFFFFFF;
    ReaderOptions ro = {0};
    pBarcodeResultArray pResults = NULL;
    ro.llBarcodeFormat = worker->llFormat;
    ro.iMaxBarcodesNumPerPage = iMaxCount;
 
    // decode barcode image
    int ret = 0;
    if (worker->buffer) 
    {
        ret = DBR_DecodeStream(worker->buffer, worker->size, &ro, &pResults);
    }
    else
    {
        ret = DBR_DecodeFile(worker->filename, &ro, &pResults);
    }
     
    if (ret)
        printf("Detection error code: %d\n", ret);
 
    // save results to BarcodeWorker
    worker->errorCode = ret;
    worker->pResults = pResults;
}

uv_after_work_cb回调函数用来在主线程显示结果:

/*
 *  uv_after_work_cb
 */
static void DetectionDone(uv_work_t *req,int status)
{
    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope(isolate);
 
    // get the reference to BarcodeWorker
    BarcodeWorker *worker = static_cast<BarcodeWorker *>(req->data);
 
    // get barcode results
    pBarcodeResultArray pResults = worker->pResults;
    int errorCode = worker->errorCode;
    int count = pResults->iBarcodeCount;
    pBarcodeResult* ppBarcodes = pResults->ppBarcodes;
    pBarcodeResult tmp = NULL;
 
    // array for storing barcode results
    Local<Array> barcodeResults = Array::New(isolate);
 
    for (int i = 0; i < count; i++)
    {
        tmp = ppBarcodes[i];
 
        Local<Object> result = Object::New(isolate);
        result->Set(String::NewFromUtf8(isolate, "format"), String::NewFromUtf8(isolate, GetFormatStr(tmp->llFormat)));
        result->Set(String::NewFromUtf8(isolate, "value"), String::NewFromUtf8(isolate, tmp->pBarcodeData));
        barcodeResults->Set(Number::New(isolate, i), result);
    }
 
    // release memory of barcode results
    DBR_FreeBarcodeResults(&pResults);
 
    // run the callback
    const unsigned argc = 1;
    Local<Value> argv[argc] = {barcodeResults};
    Local<Function> cb = Local<Function>::New(isolate, worker->callback);
    cb->Call(isolate->GetCurrentContext()->Global(), argc, argv);
 
    // release memory of BarcodeWorker
    delete worker;
}

修改dbr.js调用异步接口:

var dbr = require('./build/Release/dbr');
var readline = require('readline');
var fs = require('fs');
var barcodeTypes = 0x3FF | 0x2000000 | 0x8000000 | 0x4000000; // 1D, QRCODE, PDF417, DataMatrix
 
function decodeFileAsync(fileName) {
    dbr.decodeFileAsync(
        fileName, barcodeTypes,
        function(msg) {
            var result = null;
            for (index in msg) {
                result = msg[index]
                console.log("Format: " + result['format']);
                console.log("Value : " + result['value']);
                console.log("##################");
            }
        }
    );
}
 
var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});
 
rl.question("Please input a barcode image path: ", function(answer) {
    decodeFileAsync(answer);
    rl.close();
});

源码

https://github.com/yushulx/nodejs-barcode-for-win-linux-mac

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值