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

Make Nested Async Functions Nested Sync Functions

本文关键字:函数 嵌套 同步 异步      更新时间:2023-09-26

我正在开发一个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.
    })

JavaScript本质上是单线程的,而"异步"意味着"事件队列后面的某个地方",真的。

正因为如此,没有办法等到异步任务结束:你必须停止运行以启动下一个异步任务。唯一的方法是在异步任务结束时添加要调用的代码,称为异步(或回调)链接。

然而,值得庆幸的是,有一些框架可以帮助您以更有组织的方式构建这些东西。其中一个框架就是Promises。我将让你访问该链接以获得基本介绍。

首先,让我们"承诺"所需的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调用,或者用历史条目解析,或者用错误消息拒绝

Promise的意义在于你可以在它上面使用.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
        });
      }
    });
  });
}

所以,我们有两个承诺返回一个数组的结果。如何将它们联系在一起?

首先,我假设你没有打破内部循环(所以我"并行"运行"getvisitors")。我们用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()将在执行任何代码之前将所有结果收集到一个单一的、整体的二维数组中,并且您不能过早地中断内部循环。

您可以将其修改为顺序执行,而不是收集数组然后处理它。

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代码更容易或更干净是有争议的,但至少它是手动链接回调的另一种选择。

如果您想支持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
                ......................
            }
        })
    }
})