Azure移动服务共享脚本中未调用回调

时间:2014-03-04 21:55:46

标签: javascript node.js azure callback azure-mobile-services

在重构了一些脚本并将一些函数放在共享文件夹中的单独自定义模块后,我遇到了一个非常奇怪的问题。甚至不知道如何正确描述它。想想最好的方法是通过一个例子来展示它。我提前道歉,我会在这里发布很长一段代码。我只是不知道我怎么解释我的问题。

我们有几个共享脚本,其中包含一些常见功能:

shared/commons-util.js - 几个方便的实用功能,其中就是这个:

function getErrorHandler(errMessage, callback) {
  return function(err) {
    console.log('inside error handler')
    console.error(errMessage + '\nCause: ', err)
    if (callback instanceof Function) {
      callback(err)
    }
  }
}

exports.getErrorHandler = getErrorHandler

shared/commons-db.js - 用于处理数据库表的辅助函数:

var waitingTime = +process.env.throttlingWaitTime || 0;
var retryCount = +process.env.throttlingRetryCount || 0;

function refreshThrottlingLimit() {
  var throttlingLimit = +process.env.throttlingLimit || 0
  if (LMD_GLOBALS.throttling.limit != throttlingLimit) {
    LMD_GLOBALS.throttling.available += throttlingLimit - LMD_GLOBALS.throttling.limit
    LMD_GLOBALS.throttling.limit = throttlingLimit
  }
}

function throttledWrite(operation, table, data, callbacks, retriesLeft) {
  refreshThrottlingLimit()
  if (LMD_GLOBALS.throttling.limit && LMD_GLOBALS.throttling.available > 0) {
    LMD_GLOBALS.throttling.available--

    try {
      console.log('Executing ' + operation + ' on table: ' + table.getTableName() + ', data: ', data, 'callbacks: ', callbacks)

      table[operation](data, {
        success: function(result) {
          try {
            console.log('throttledWrite: SUCCESS')
            LMD_GLOBALS.throttling.available++
            if (callbacks && callbacks.success) callbacks.success(result)
          } catch (e) {
            console.error('Exception in success callback', e)
          }
        },
        error: function(err) {
          try {
            console.log('throttledWrite: ERROR')
            LMD_GLOBALS.throttling.available++
            err.table = table.getTableName()
            err.operation = operation
            err.data = data
            if (callbacks && callbacks.error) callbacks.error(err)
          } catch (e) {
            console.error('Exception in error callback', e)
          }
        }
      })

      console.log(operation + ' started...')
    } catch (e) {
      console.error('Exception starting ' + operation, e)
    }
  } else if (retriesLeft > 0) {
    setTimeout(throttledWrite, waitingTime, operation, table, data, callbacks, retriesLeft - 1)
  } else {
    if (callbacks && callbacks.error) callbacks.error(new Error('Aborting ' + operation + ' operation (waited for too long)'))
  }
}

exports.throttledInsert = function(table, data, callbacks) {
  throttledWrite('insert', table, data, callbacks, retryCount)
}
exports.throttledUpdate = function(table, data, callbacks) {
  throttledWrite('update', table, data, callbacks, retryCount)
}

shared/commons.js - 只需将这两个模块结合起来:

exports.db = require('../shared/commons-db.js')
exports.util = require('../shared/commons-util.js')

我们在预定的脚本executeBackgroundJobs.js中使用此模块:

var commons = require('../shared/commons.js');

// Same high-order function as in `commons-util.js`. Only difference is that it is defined locally, not in the separate module.
function getErrorHandler(errMessage, callback) {
  return function(err) {
    console.log('inside error handler')
    console.error(errMessage + '\nCause: ', err)
    if (callback instanceof Function) {
      callback(err)
    }
  }
}

function executeBackgroundJobs() {
  test();
}

function test() {
  console.log('Testing paranormal activity in Azure');

  var testTable = tables.getTable('test');

  var callback = function(res) {
    console.log('Test OK', res)
  }

  var errMessage = 'Could not write to `test` table';

  var errHandler_1 = commons.util.getErrorHandler(errMessage, callback); // First handler we get from high-order function from custom module
  var errHandler_2 = getErrorHandler(errMessage, callback); // Second handler must be the same, just getting from local function not from the external module

  // This log lines just show that this two functions are identical
  console.log('errHandler_1: ', errHandler_1);
  console.log('errHandler_2: ', errHandler_2);

  // We are calling `throttledUpdate` two times with two different error handlers for each invocation

  commons.db.throttledUpdate(testTable, {
    id: 'test-01',  // Data object is intentionally illegal, we want update to fail
    someColumn: 'some value #1'
  }, {
    success: callback,
    error: errHandler_1
  });

  commons.db.throttledUpdate(testTable, {
    id: 'test-02',  // Data object is intentionally illegal, we want update to fail
    someColumn: 'some value #2'
  }, {
    success: callback,
    error: errHandler_2
  });

}

现在这是这项工作的一次调用的输出:

1:throttledWrite的第一个电话:

INFORMATION:
Executing update on table: test, data:  { id: 'test-01', someColumn: 'some value #1' } callbacks:  { success: [Function], error: [Function] }

2:开始test()。奇怪的是,日志的顺序不正确,因此到目前为止这个函数被同步调用(或者至少我相信如此)。这是否意味着Azure中的日志记录发生在不同的线程中?无论如何,这不是我们的主要问题。

INFORMATION:
Testing paranormal activity in Azure  

3:这里我们只展示第一个处理程序的主体

INFORMATION:
errHandler_1:  function (err) {
    console.log('inside error handler')
    console.error(errMessage + '\nCause: ', err)
    if (callback instanceof Function) {
      callback(err)
    }
  }

4:这里我们只展示第二个处理程序的主体

INFORMATION:
errHandler_2:  function (err) {
    console.log('inside error handler')
    console.error(errMessage + '\nCause: ', err)
    if (callback instanceof Function) {
      callback(err)
    }
  }

5:table.update方法在第一个throttledWrite成功启动(无例外)

INFORMATION:
update started... 

6:throttledWrite的第二次电话:

INFORMATION:
Executing update on table: test, data:  { id: 'test-02', someColumn: 'some value #2' } callbacks:  { success: [Function], error: [Function] }

7:table.update方法在第二个throttledWrite成功启动(无例外)

INFORMATION:
update started... 

9:调用错误处理程序

INFORMATION:
inside error handler  

10:仅来自 第二个throttledWrite

ERROR:
Could not write to `test` table
Cause:  { _super: undefined,
  message: 'Could not save item because it contains a column that is not in the table schema.',
  code: 'BadInput',
  table: 'test',
  operation: 'update',
  data: { id: 'test-02', someColumn: 'some value #2' } }

11:这就是全部

INFORMATION:
Test OK { _super: undefined,
  message: 'Could not save item because it contains a column that is not in the table schema.',
  code: 'BadInput',
  table: 'test',
  operation: 'update',
  data: { id: 'test-02', someColumn: 'some value #2' } }

首先,我无法理解的最奇怪的事情是为什么在第一次throttledWrite调用中没有调用回调。

除此之外,即使在调用回调的第二个throttledWrite中,也没有从table.update - s error回调中写入日志。请记住throttledWrite函数:

中的这一行
        error: function(err) {
          try {
            console.log('throttledWrite: ERROR')
            LMD_GLOBALS.throttling.available++
            err.table = table.getTableName()
            err.operation = operation
            err.data = data
            if (callbacks && callbacks.error) callbacks.error(err)
          } catch (e) {
            console.error('Exception in error callback', e)
          }
        }

我必须补充一点,这不是我第一次遇到这个问题的实际代码。我试着在发布之前缩短代码。所以你可能不完全理解某些方法的目的。没关系,这段代码只是为了演示。

就是这样。我有三个无法解释的谜团:

  1. 为什么不调用回调(在第一种情况下)?
  2. 为什么console.log('throttledWrite: ERROR')没有在日志中写任何内容(当我确定,它被调用并且成功了)?
  3. 不太重要,但仍然很有趣,为什么日志不正确?

0 个答案:

没有答案