使嵌套异步函数嵌套同步函数

时间:2015-10-09 04:12:35

标签: javascript asynchronous google-chrome-extension

我正在开发一个使用两个嵌套异步函数的chrome扩展。但是我希望这些函数是同步的,但是没有一个参数使它同步,API只有async选项。如果可能的话,我需要这两个函数具有嵌套的同步函数行为。告诉我你是否有意义。以下是代码的主要部分:

// Track the number of callbacks from chrome.history.getVisits()
// that we expect to get.  When it reaches zero, we have all results.
chrome.history.search({
        'text': '',              // Return every history item....
        'startTime': oneWeekAgo,  // that was accessed less than one week ago.
        'maxResults': 999999999
    },
    function (historyItems) {
        // For each history item, get details on all visits.;

        for (var i = 0; i < historyItems.length; ++i) {
           Edited - 0 - I need some code here too.
           chrome.history.getVisits({url: historyItems[i].url}, function           (visitItems)   {
                //1 - I want this to happen first, but it happens after 2

                for (var j = 0; j < visitItems.length; ++j) {
                    //Do something that depends on the first function
                }
            })
            //2 -i want this to happen second and after 1, but it happens first, since chrome.history.getvisits is async.
        }
        //3- I want this to happen third, when the two loops are complete.
    })

2 个答案:

答案 0 :(得分:2)

JavaScript本质上是单线程的,并且&#34;异步&#34;意味着&#34;在事件队列后面的某个地方&#34;,真的。

因此,在异步任务结束之前无法等待:您必须结束运行才能启动下一个异步任务。唯一的方法是添加要在异步任务结束时调用的代码,称为异步(或回调)链接。

但是,幸运的是,有一些框架可以帮助您以更有条理的方式构建这些东西。其中一个框架是Promises。我将让您访问该链接以获取基本介绍。

首先,让我们&#34;宣传&#34;所需的API调用。第一个:

function historyLastWeek() {
  return new Promise(function(resolve, reject) {
    chrome.history.search({
      'text': '',
      'startTime': oneWeekAgo, // don't forget that part
      'maxResults': 999999999
    }, function(historyItems) {
      if(chrome.runtime.lastError) {
        reject(chrome.runtime.lastError.message);
      } else {
        resolve(historyItems);
      }
    });
  });
}

上面的代码返回一个Promise,它将运行chrome.history.search API调用,并使用历史记录项 resolve ,或者拒绝并返回错误消息。

承诺的一点是,你可以使用.then()来链接电话。

让我们也宣传chrome.history.getVisits(注意,它需要一个历史项目,因为我们想要):

function getVisits(historyItem) {
  return new Promise(function(resolve, reject) {
    chrome.history.getVisits({url: historyItem.url}, function(visitItems) {
      if(chrome.runtime.lastError) {
        reject(chrome.runtime.lastError.message);
      } else {
        resolve({
          historyItem: historyItem, // let's keep track of it
          visitItems: visitItems
        });
      }
    });
  });
}

因此,我们有2个承诺返回一系列结果。如何tie them together

首先,我假设你不打破内循环(所以我运行&#34; getVisits&#34;在&#34; parallel&#34;)。我们Promise.all就是这样。我们来看看..

historyLastWeek().then(function(historyItems) {
  return Promise.all(
    // map() returns the array of results of applying a function 
    //   to all members of the array
    historyItems.map(getVisits)
  );
}).then(function(results) {
  // results is an array of objects
  results.each(function(result) {
    // here, result.historyItem is the history item,
    //   and result.visitItems is an array of visit items
    /* here goes your code #1 */
    result.visitItems.each(function(visitItem) {
      /* here goes your "Do something that depends on the first function" code */
    });
    /* here goes your code #2 */
  });
  /* here goes your code #3 */
}).catch(function(errorMsg) {
  // oh noes
});

如果您需要在代码#3之后执行某些操作,那么您还需要宣传最后一个功能,并添加另一个.then()

此代码具有不幸的属性:由于JavaScript不是懒惰的,all()将在执行任何代码之前在单个单片二维数组中收集所有结果,并且您不能早点打断内循环。

您可以将其修改为execute sequentially,而不是收集数组然后进行处理。

historyLastWeek().then(function(historyItems) {
  return historyItems.reduce(function(sequence, historyItem) {
    return sequence.then(function() {
      return getVisits(historyItem);
    ).then(function(result) {
      // here, result.historyItem is the history item,
      //   and result.visitItems is an array of visit items
      /* here goes your code #1 */
      result.visitItems.each(function(visitItem) {
        /* here goes your "Do something that depends on the first function" code */
      });
      /* here goes your code #2 */
      // Gotta return some promise
      return Promise.resolve();
    });
  }, Promise.resolve());
}).then(function() {
  /* here goes your code #3 */
}).catch(function(errorMsg) {
  // oh noes
});

请参阅上面的链接,了解所有相关方法的解释。

如果它比非Promise代码更容易或更清晰,那么它是值得商榷的,但至少它可以替代手动链接回调。

答案 1 :(得分:1)

如果你想在32之前支持版本的Chrome,那么你可以做的就是将代码23移到内部回调中:

function (historyItems) {
    var lastItemIndex = historyItems.length - 1;
    for (var i = 0; i <= lastItemIndex; ++i) {
       chrome.history.getVisits({url: historyItems[i].url}, function(visitItems) {
            // 1
            for (var j = 0; j < visitItems.length; ++j) {
                //Do something that depends on the first function
            }
            // 2
            ......................
            if (i == lastItemIndex) {
                // 3
                ......................
            }
        })
    }
})