简体   繁体   中英

Asynchronous operations in Javascript.

I am creating a React app that involves several calls to some webapi REST services to do my thing. Part of the app, is the approval flow of some requests. There is a specific role that can create these flows with a UI that consists of:

  1. A table that lists the steps of the procedure sorted by cardinality (that means the order). The steps have the actor(s) and the status as well.
  2. The buttons on each row to arrange up/down
  3. Buttons on each row to delete the respective row
  4. A button to add a new step.

What I do is allow the user to make changes using Javascript (mostly array operations), while populating an actionbuffer array with the action and the respective data. Eg.

this.actionsBuffer.push({
        action: "ADD_STEP",
        data: next
    });

When the user is happy with the arrangement, she can press the Accept button. What it does is iterating the actionsBuffer array and execute the appropriate REST service which is determined by the action field.

I know my description might seem too detailed but I wanted you to know the context.

Question: My question now is that since the calls are asynchronous how can I guarantee that the actions will execute by this order.

Some code snippets:

This iterates and calls determineAction

onAccept: function (e) {
        e.preventDefault();
        var self = this;
        //console.log("Gonna save:",JSON.stringify(this.state.workflow));

        var ret=null;
        // First we do actions in actionsBuffer
        for(var i=0;i<this.actionsBuffer.length;i++)
        {
            ret = self.determineAction(this.actionsBuffer[i]);
            if (ret==false)
                break;
           else
                this.actionsBuffer.splice(i,1);
            ret=null;
        }


        this.saveAll();
    },

And determineAction. Pardon the debugging console messages

determineAction: function (action) {

        var url="";
        var verb="";

        switch(action.action)
        {
            case "ADD_STEP":
                delete action.data.ActorList;
                url=this.props.server+"/workflows/"+this.props.workflowid+"/steps";
                verb="POST";
                break;
            case "DELETE_STEP":
                url=this.props.server+"/workflows/"+this.props.workflowid+"/delete/";
                verb="POST";
                break;
        }

        console.log("Going to call url:",url," with varb:",verb," and data:",action.data);
        $.ajax({
            type: verb,
            url: url,
            data: JSON.stringify(action.data),
            processData:false,
            contentType: 'application/json'
        })
            .success(function(data) {
                return true;
                //self.props.onclose(self.state.workflows.WorkflowId);
            })
            .error(function(jqXhr) {
                console.log(jqXhr);
                return false;
            });
    },

You are not waiting for determineAction to finish. Make it return a promise, and wait for it where you are calling it. Also your loop has to be asynchronous. I've created an attempt that may not be exactly what you need but shows you the direction that you should move.

onAccept: function (e) {
    e.preventDefault();
    var self = this;


    var ret=null;
    // First we do actions in actionsBuffer
    var i = 0;
    function makeRequest() {
        self.determineAction(self.actionsBuffer[i]).success(function() {
            i++;
            if (i >= (self.actionsBuffer.length) {
                 self.saveAll();
            } else {
                makeRequest();
            }

        }).error(function(){
            self.saveAll();
        })
    }
    makeRequest()

    this.saveAll();
},


determineAction: function (action) {

    var url="";
    var verb="";

    switch(action.action)
    {
        case "ADD_STEP":
            delete action.data.ActorList;
            url=this.props.server+"/workflows/"+this.props.workflowid+"/steps";
            verb="POST";
            break;
        case "DELETE_STEP":
            url=this.props.server+"/workflows/"+this.props.workflowid+"/delete/";
            verb="POST";
            break;
    }

    console.log("Going to call url:",url," with varb:",verb," and data:",action.data);
    return $.ajax({
        type: verb,
        url: url,
        data: JSON.stringify(action.data),
        processData:false,
        contentType: 'application/json'
    });
},

Rather than iterating over your array of actions synchronously with a for loop. Instead, treat it as a queue.

  1. Take the first item from the queue and execute it.
  2. When the asynchronous work finishes, take another item from the queue.
  3. Repeat until you've cleared the queue.

Here's a simple example.

function processActions(actionQueue) {
  if(actionQueue.length == 0) return;

  // take the first action from the queue
  var action = actionQueue[0];

  // assuming determineAction() returns a promise
  determineAction(action)
    .then(function() {
      var remainingActions = actionQueue.slice(1);

      // we know this action has completed, so we can pass
      // the remaining actions to be processed
      processActions(remainingActions);
    });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM