在循环中调用异步函数的层次结构?

时间:2017-11-16 16:24:43

标签: javascript ajax asynchronous dojo

有一个异步调用我将查询作为服务上的数据库,但是这个服务有一次可以输出的数量的限制,所以我需要检查它是否达到了极限它发送的结果并重复查询,直到它没有。

同步样机:

var query_results = [];

var limit_hit = true; #While this is true means that the query hit the record limit
var start_from = 0; #Pagination parameter

while (limit_hit) {
    Server.Query(params={start_from : start_from}, callback=function(result){
        limit_hit = result.limit_hit;
        start_from = result.results.length;
        query_result.push(result.results);
    }
}

显然上面的内容不起作用,我在这里看到了一些关于这个问题的其他问题,但是当你需要每次迭代等待最后一次完成时,他们都没有提到要做什么。事先不知道迭代次数。

如何将上述异步转为?我使用承诺/类似延迟的逻辑开放答案,但最好是干净的。

我可能会想到使用等待/超时这样做的一种奇怪而可怕的方式,但必须有一种干净,聪明和现代的方法来解决它。

另一种方法是进行"预查询"要知道手头的功能数量,以便了解循环次数,我不确定这是否正确。

这里我们有时会使用Dojo,但是我发现的示例并没有解释当你有一个未知数量的循环时要做什么https://www.sitepen.com/blog/2015/06/10/dojo-faq-how-can-i-sequence-asynchronous-operations/

5 个答案:

答案 0 :(得分:4)

虽然已有很多答案,但我相信async / await是最干净的方法。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

你可能需要巴贝尔

https://babeljs.io/

JS异步逻辑语法从callback更改为promise然后更改为async/await,它们都做同样的事情,当callback嵌套很多时我们需要像链一样的东西然后承诺来,当promise进入循环时,我们需要一些东西使链更简单,然后async/await来。但并非所有浏览器都支持新语法,因此babel将新语法编译为旧语法,然后您始终可以使用新语法进行编码。



getData().then((data) => {
  //do something with final data
})

async function getData() {
  var query_results = [];

  var limit_hit = true;
  var start_from = 0;
  
  //when you use await, handle error with try/catch
  try {
    while (limit_hit) {
      const result = await loadPage(start_from)
      limit_hit = result.limit_hit;
      start_from = result.results.length;
      query_result.push(result.results);
    }
  } catch (e) {
    //when loadPage rejects
    console.log(e)
    return null
  }
  return query_result
}

async function loadPage(start_from) {
  //when you use promise, handle error with reject
  return new Promise((resolve, reject) => Server.Query({
    start_from
  }, (result, err) => {
    //error reject
    if (err) {
      reject(err)
      return
    }
    resolve(result)
  }))
}




答案 1 :(得分:3)

如果你想使用一个循环,那么我认为没有Promise就没有(干净的)方法去做。

不同的方法如下:



var query_results = [];

var start_from = 0;

funciton myCallback(result) {  
  if(!result) {
    //first call
    Server.Query({ start_from: start_from}, myCallback);
  } else {
    //repeated call
    start_from = result.results.length
    query_result.push(result.results);
    
    if(!result.limit_hit) {
      //limit has not been hit yet
      //repeat the query with new start value
      Server.Query({ start_from: start_from}, myCallback);
    } else {
        //call some callback function here
    }
  }
}

myCallback(null);




您可以将此调用称为递归,但由于Query是异步的,因此您不应该遇到调用堆栈限制等问题。

在ES6环境中使用promises,您可以使用async / await。我不确定这是否可以用dojo。

答案 2 :(得分:2)

在编写速率限制器或队列之前,您不了解回调;)技巧是使用计数器:在异步请求之前递增计数器,并在获得响应时递减计数器,然后您将知道如何许多要求都在“飞行中”。

如果服务器被阻塞,您想将该项目放回队列中。

您需要考虑许多事项: 如果进程被杀死,队列会发生什么? 在发送另一个请求之前要等多久? 确保多次调用回调! 你应该重试多少次? 放弃前要等多久? 确保没有松散的末端! (永远不会调用回调)

当考虑所有边缘情况时,您将拥有相当长且不那么优雅的解决方案。但是你可以把它抽象成一个函数! (返回Promise或你想要的任何东西)。 如果您有用户界面,还需要显示加载栏和一些统计信息!

答案 3 :(得分:0)

您必须每次都等待服务器响应。这是一个封装的方法

div class="stars" span title="Money Locker :  Pulsa Gratis average
 rating 3.9" style="width:78%" span div


    $item['stars'] = $article->find('div.stars span', 0)->style;
                $item['stars'] = str_replace("width:", "", $item['stars']);

答案 4 :(得分:-1)

您可以使用生成器函数Generators来实现此目的 对于POC:

一些基础知识   - 使用星号*定义生成器   - 它公开了一个返回下一个值的下一个函数   - 生成器可以在内部暂停yield语句,并可以通过调用next()从外部恢复   - 而(true)将确保在limit到达之前不执行生成器

function *limitQueries() {
  let limit_hit = false;
  let start_from  = 0;
  const query_result = [];

  while (true) {
    if (limit_hit) {break;}
    yield Server.Query(params={start_from : start_from}, 
        callback=function* (result) {
        limit_hit = result.limit_hit;
        start_from = result.results.length;
        yield query_result.push(result.results);
    }
  }
}

显然,生成器函数保持自己的状态。生成器函数公开两个属性{ value, done },您可以像这样调用它

const gen = limitQueries();
let results = [];
let next = gen.next();

while(next.done) {
  next = gen.next();
}
results = next.value;

您可能必须触摸Server.Query方法来处理生成器回调。希望这可以帮助!干杯!