重复执行一会儿循环延迟,直到其中的ajax调用完成为止

时间:2019-03-15 17:45:45

标签: javascript jquery ajax while-loop

在我解释要做什么之前,这是我正在编码的MCV

for c_file in /dir1/*.fa; do
  b_file=/dir2/$(basename "$c_file" .fa).rem
  echo "$c_file" "$b_file"
done

因此,我有一个文本区域,其中包含未知数量的BLOB对象。我首先尝试使用regexp查找这些BLOB对象,然后使用一个名为uploader.php的PHP文件将它们上传到服务器。文件上传后,它将返回上传文件的URL,我想用文本区域中上传文件的URL替换BLOB URL,然后将文本区的最终内容提交给服务器以进行进一步处理。

事实证明,当我运行代码时,仅将regexp的最后一个实例替换为其上载的URL。其他人保持原样。我怀疑这是因为while循环不会等待ajax请求完成。尝试提交表单时,我遇到了类似的问题,我按照此answer中的建议解决了问题,但这次我不知道如何解决此问题。

任何想法都值得赞赏

更新:我试图使ajax同步工作,但是我的浏览器说它已被弃用,并且不起作用。

2 个答案:

答案 0 :(得分:1)

所以。我在评论中说,您可以使用async / await,并提供链接。现在,我将尝试教您如何使用Promise和XMLHttpRequest。

第一件事。我会使用自己的名为PromiseReq的“库”(不是真正的库,只是3个新命令),它具有返回Promises的XMLHttpsRequest。 您将需要两个功能: sendToServer(config, data)getServerFile(config)。它们按照其名称的含义进行操作。(sendToServer当时还不太好,但是稍后我会进行改进)。他们只是使用Promises作为回报。他们以非常简单的方式工作。 Code @ Github

但是它仅为我使用而设计,因此它不是非常灵活(尽管我希望我会在某个时候加以改进)。

所以我们需要学习如何使用Promises。

首先,您需要了解Promise是什么以及我们为什么使用它。

然后您可以创建一个这样的文件:

let pmq = new Promise((res,rej)=>{
// PROMISE BODY HERE
});

这里是第一个警告。以这种方式做出的承诺不支持将退货作为解决Promise的一种方式!您必须使用res()

某些函数只是返回它们(例如fetch()),我们可以在调用函数后立即处理它们。

现在pmq是我们的承诺。 您可以使用pmq.then(callback)处理在res()调用诺言主体中某处时将发生的情况,并且可以使用pmq.catch(callback)处理在调用rej()时发生的情况。请记住,.catch(cb).then(cb)返回一个Promise,因此您可以安全地链接多个.then(),最后添加.catch(),它将处理来自每个.then()

例如:

pmq = fetch("file.txt");
pmq.then(e=>console.log(e.json())).then(console.log).catch(console.error);

那里有一个大音符。

此事件的触发顺序。

例如,如果rP()比日志"A"等待1秒然后解析,则此代码:

let a = rP();
a.then(_=>console.log("B")).catch(console.error);
console.log("C");

将导致:

C
A
B

为此,需要async/await来完成此操作。

为此,您必须使用关键字async创建一个异步函数。

let fn = async ()=>{} 

这是第二件事。异步函数总是返回Promise。这是您创造承诺的第二种方式。您只是不只使用res()rej()来使用returnthrow

现在我们可以在fn()内部进行呼叫:

let a = await rP().then(_=>console.log("B")).catch(console.error);
console.log("C");

我们将得到我们的

A
B
C

现在。如何与XMLHttpRequest一起使用?

您需要使用简单的XMLHttpRequest创建新的Promise:

let xmlp = (type, path,data) => return new Promise((res,req)=>{
    let xhr = new XMLHttpsRequest();
    xhr.open(type, path, true); // true implies that is it asynchronous call
    //...
    xhr.send(data);
});

现在什么时候解决? XMLHttpRequest具有有用的event properties and events。最适合我们的情况的是onreadystatechange

您可以像这样使用它:

xhr.onreadystatechange = _=>{
    if(xhr.readyState === 4 && xhr.status === 200) // Everything went smoothly
        res(xhr.responseText);
    else if(shr.readyState === 4 && xhr.status !== 200) // Something went wrong!
        rej(xhr.status);
}

然后要获取数据,您可以

  1. Async/Await
// INSIDE ASYNC FUNCTION
let resData = await xmpl("GET", "path.txt", null).catch(console.error);
  1. .then()
let resData;
xmpl("GET", "path.txt", null).then(r=>{
    resData = r;
    // REST OF WHOLE FUNCTION TO ENSURE THAT resData HAS BEEN SET
})
.catch(console.error);

您还可以使用xmpl()发送数据。

xmpl("POST", "observer.php", "Data to send to observer.php!")
.then(whatToDoAfterSendFN);
/* 
to get such data in PHP you have to use 
$post = file_get_contents('php://input');
*/

我知道这个答案有点杂乱无章,但是我不知道该怎么写:P对不起。

答案 1 :(得分:1)

似乎您并不需要同步(我看不到最好让异步动作看起来是同步的情况),而只需要顺序即可。

可以通过使用回调(可重写为Promise,然后可重写为async / await方法,使回调顺序执行),

// myString is made global for simplicity
var myString;
function uploadBlob(myBlob, addr, callback) {
  var data = new FormData();
  data.append('file', myBlob);

  $.ajax({
    url: "uploader.php",
    type: 'POST',
    data: data,
    contentType: false,
    processData: false,
    success: function(data) {
      // file uploaded OK, replace the blob expr by the uploaded file url(data)
      myString = myString.replace("blob:" + addr, data);
      callback();
    },
    error: function() {
      // error, the uploaded most likely failed, we leave myString alone
      // or alternatively replace the blob expr by empty string
      // because maybe we dont want to post blob in the final form submit
      // uncomment if needed
      // myString = myString.replace("blob:" + addr, "");
      callback();
    }
  });
}

function getBlobAndUpload(addr, callback) {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', 'blob:' + addr, true);
  xhr.responseType = 'blob';
  xhr.onload = function(e) {
    if (this.status == 200) {
      var myBlob = this.response;
      uploadBlob(myBlob, addr, callback);
    } else {
      // error, but callback anyway to continue processing
      callback();
    }
  };
  xhr.send();
}

function processAddresses(addresses, callback, current) {
  var index = current || 0;
  // all addresses processed?
  if (index >= addresses.length) {
    // yes no more address, call the callback function
    callback();
  } else {
    var addr = addresses[index];
    // once the get/upload is done the next address will be processed
    getBlobAndUpload(addr, function() {
      processAddresses(addresses, callback, index + 1);
    });
  }
}

$("#button").submit(function(event) {
  event.preventDefault();
  var formData = new FormData(this);
  var addresses = [];
  // initialize both localString and myString to the content of the textArea
  // localString will be used to extract addresses,
  // while myString will be mutated during the upload process
  var localString = myString = $('#textarea').val();
  var myRegexp = /src="blob:([^'"]+)"/gm;
  match = myRegexp.exec(localString);

  // collect all addresses first
  while (match != null) {

    addr = match[1];

    addresses.push(addr);
    match = myRegexp.exec(localString);
  }
  // initiate sequential processing of all addresses, and
  // pass the callback function triggering the form submit
  processAddresses(addresses, function() {
      // all the successfully uploaded blob exprs in my string should
      // be now replaced by the remote file url now (see commented part
      // in upload blob error for a variation of the feature
      formData.set('textarea', myString);
      submitForm(formData);
  });
});