我想以异步方式执行常规函数

时间:2016-05-10 10:41:36

标签: javascript asynchronous async-await

考虑这个样本(比如这是模块)

function Calculator(value){
    return {
        add: function(value2){
            return: {
                value: function(){
                    return value + value2;
                }
            }
        }
    }
}

这是一个类,初始化时需要参数,样本用法:

var Calculator = require('calculator_app_module');
var myCalc = new Calculator(1); // initialized with 1

myCalc.add(2).value(); //  === 3;

这显然是预期的,我想要的是以异步方式执行add函数,就像那样

var Calculator = require('calculator_app_module');
var myCalc = new Calculator(1); // initialized with 1

myCalc.add(2).value() ==== 3  // this executes in 2secs (async)
                              // and then returns result

我想修补 Calculator.add 方法,以便它可以使用异步

function patch(module){ //module is Calculator class
    var oldAdd = Calculator.add;
    Calculator.add = function(){
        // some magic
        // trigger event or whatever
        oldAdd.apply(Calculator, arguments);
    }
}

INDEX.JS

var Calculator = require('calculator_app_module');
var calc = new Calculator(1);

calc.add(2).value() === 3;     // equalize within 2 seconds
                               // after async call is done
calc.add(2).value().equal(3);  // also legit

问题是calc.add(n)返回在异步调用中未定义的新函数value,是否有办法获取add的调用fn并在结果到来时将其调回/ p>

更新

@Zohaib Ijaz 回答之前,你无法修改包的内容/逻辑,只能扩展/补丁,包必须返回相同的API,但是以承诺的方式,没有代码破解

calc.add(2).value() === 3;    // sync code
calc.add(2).value() === 3;    // async code
calc.add(2).value().equal(3); // async code

如何实现

更新

根据 @Zohaib Ijaz 评论,这也是合法的

myCalc.add(2).value().equal(3); //async

要点是将同步转换为异步而不破坏包,但扩展结果

2 个答案:

答案 0 :(得分:2)

如果您通过调用一系列方法来请求结果,例如:

a = myCalc.add(2).value();

或者这个:

myCalc.add(2).value().equal(3);

然后 没有可能性 来检索和使用仅异步可用的结果(即稍后,在评估语句之后)。请注意,异步涉及将某些事件放入事件队列中。当前执行的代码必须首先完成(即,直到调用堆栈为空),然后才能处理该事件。

以上语法仅对即时评估有用。为了处理异步结果,您需要在某处提供回调函数,以便了解这些结果。

因此,在add方法中使用异步依赖,您的代码可以提供对add方法的回调,它在收到异步结果时会调用它:

myAsyncCalc.add(2, function (added) {
    a = added.value();
});

或者,当使用promises(非常适合使用)时,add方法将返回一个对象,您可以为其指定相同的回调:

myAsyncCalc.add(2).then(function (added) {
    a = added.value();
});

请注意,回调函数不是当前正在执行的代码的一部分。它只是一个函数引用,可以在以后的异步事件中用于回调。但这将是单独执行序列的一部分,该序列仅在处理内部事件队列并且已处理触发该执行序列的事件时开始。

如果这不是一个可接受的解决方案,并且你真的需要以前的语法以某种方式考虑异步生成的结果,那么你是没有希望的:它是不可能的,因为它真的代表同步代码执行。

包装您的对象

您写道,您不能修改包的内容,但只能扩展它。

一种方法是使用proxies。 这个想法是你捕获对add方法的引用,然后返回 您自己的方法的改编版本,可以选择仍然调用原始方法。 有关示例,请参阅上面引用的MDN文章。

同步调用HTTP请求

如果你真的想写这样的代码:

a = myCalc.add(2).value();

即使add的实现执行HTTP请求,您也可以查看同步发出HTTP请求。但应该指出的是,这被认为是不好的做法。

代码示例

以下是以三种方式执行添加的代码:

  • 未修改(同步)
  • 使用异步HTPP调用
  • 使用同步HTTP调用

对于两个修改版本,使用代理模式。对于异步示例,使用Promise模式使用回调。

代码:



// code in module is not modified
function Calculator(value){
    return {
        add: function(value2){
            return {
                value: function(){
                    return value + value2;
                }
            }
        }
    }
}

// standard object creation
var myCalc = new Calculator(1); // initialized with 1

// Create a proxy for the above object, which will expose 
// an asynchronous version of the "add" method. Note that the
// "myCalc" object is not modified.
var myCalcHttpAsync = new Proxy(myCalc, {
    get: function(myCalc, name) {
        if (name !== 'add') return myCalc[name]; // pass-through
        return function(value2) {
            return new Promise(function(resolve, reject) {
                // Define some url
                var url = 'http://api.stackexchange.com/2.2';
                // Perform HTTP request
                var request = new XMLHttpRequest();
                // Define call back for when response becomes available
                request.onload = function() {
                    if (request.readyState !== 4) return; 
                    // When async task notifies it has finished:
                    // call the original "add" method and notify those 
                    // waiting for the promise to get resolved
                    resolve(myCalc.add(value2));
                };
                // `true` as third argument makes the request asynchronous
                request.open('GET', url, true);
                request.send(null); 
            });
        };
    }
});

// Create another, alternative proxy for demonstrating 
// synchronous HTTP call:
var myCalcHttpSync = new Proxy(myCalc, {
    get: function(myCalc, name) {
        if (name !== 'add') return myCalc[name]; // pass-through
        return function(value2) {
            // Define some url
            var url = 'http://api.stackexchange.com/2.2';
            // Perform HTTP request
            var request = new XMLHttpRequest();
            // `false` as third argument makes the request synchronous
            request.open('GET', url, false);
            // code execution "hangs" here until response arrives  
            request.send(null); 
            // process response...
            var data = request.responseText;
            // .. and return the value
            return myCalc.add(value2);
        };
    }
});

// I/O

var std = document.getElementById('std');
var async = document.getElementById('async');
var sync = document.getElementById('sync');

// 1. Standard 
std.textContent = myCalc.add(2).value();

// 2. Asynchronous HTTP
myCalcHttpAsync.add(2).then(function (added) {
    // This needs to happen in a callback, otherwise it would be synchronous.
    async.textContent = added.value();
});

// 3. Synchronous HTTP 
sync.textContent = myCalcHttpSync.add(2).value();

Unmodified result: <span id="std">waiting...</span><br>
Result after asynchronous HTTP call: <span id="async">waiting...</span><br>
Result after synchronous HTTP call: <span id="sync">waiting...</span><br>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

这是我使用诺言的解决方案。

这是jsbin的链接,您可以在其中执行代码。

http://jsbin.com/qadobor/edit?html,js,console,output

function Calculator(value) {
  return {
    add: function(value2) {
      return new Promise(function(resolve, reject) {
        setTimeout(
          function() {
            resolve(value + value2);
          }, 2000);
      });
    }
  };
}

var myCalc = new Calculator(1); // initialized with 1

myCalc.add(2).then(function(ans){
  // this callback will be called after 2 seconds after promise resolve.
  console.log(ans);
});