繁体   English   中英

如何使我的代码异步? (谷歌浏览器扩展)

[英]How do I make my code asynchronous ? (Google Chrome Extension)

我正在开发一个 Google Chrome 扩展程序,它从两台服务器收集数据并将其发送到另一个服务。 我不明白如何使它异步。 请求似乎工作正常。

我在 Google 上搜索了一些解释,但只找到了一些有关超时的基本教程。 此外,产品服务器接受 Ajax 请求,交易服务器不接受(CORS 错误)。 所以我使用了 XMLHttpRequest。

document.addEventListener("DOMContentLoaded", function () {

    var createButton = document.getElementById("createButton");
    createButton.addEventListener("click", function () {
        getProducts();
        getDeals();
    }, false)


    function getProducts() {
        var list = [];
        chrome.tabs.getSelected(null, function (tab) {
            var Url = parseDealIdToUrl(tab.url)
                $.get(Url, function (data, status) {
                    const jsonData = JSON.parse(JSON.stringify(data))
                    const productArray = jsonData.data
                    productArray.forEach(product => {
                        productList.push(new Product(product.id, product.deal_id, product.product_id, product.name, product.item_price, product.quantity, product.duration, product.discount_percentage, product.currency, product.sum, product.sum_formatted))
                    });

                })
        });
    }

    function getDeals(maxPages = 1, currentPage = 1, akquises = []) {
        var akquiseList = akquises;

        if (currentPage <= maxPages) {
            var Url = dealUrl + currentPage
            var xhr = new XMLHttpRequest();
            xhr.open("GET", Url, true);
            xhr.setRequestHeader("Authorization", "Token token=")
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4) {
                    const akquiseArray = JSON.parse(xhr.responseText);
                    akquiseArray.forEach(akquise => {
                        akquiseList.push(new Akquise(akquise.name, akquise.id))
                    });
                    getDeals(handlePagination(xhr.getResponseHeader("Link")), currentPage + 1, akquiseList)
                }
            }
            xhr.send();
        }
    }
}, false)

我想调用这两个函数并等待两个列表都填满,然后将数据发送到服务。 任何想法都会帮助我!

我不太确定您所说的“使其异步”是什么意思。 正如 wOxxOm 所说,XMLHttpRequest 是一种异步方法。 你的意思是你不确定如何组合多个异步操作的结果? 为了这个答案,我假设情况就是这样。

基本异步性

为了分解异步函数的工作方式,让我们看一下代码的简化示例。 下面我们有一个调用 2 个不同异步函数的 main 函数。 当您运行此块时,您将收到一条DONE消息记录到控制台, Async 1 complete在 1 秒后Async 1 complete记录,而Async 2 complete在 1 秒后Async 2 complete记录。

// Copyright 2019 Google LLC.
// SPDX-License-Identifier: Apache-2.0

(function main() {
  doAsync1();
  doAsync2();
  console.log('DONE');
})()


function doAsync1() {
  setTimeout(() => {
    console.log('Async 1 complete');
  }, 1000);
}

function doAsync2() {
  setTimeout(() => {
    console.log('Async 2 complete');
  }, 2000)
}

在其他语句之前记录DONE的原因是因为doAsync1doAsync2是异步的——它们需要几秒钟才能完成它们的工作。 当您在main调用doAsync1()时,JS 引擎将进入doAsync1函数并开始执行行。 第一行是setTimeout调用。 这个函数接受它的第一个参数并安排它在 1000 毫秒后执行。

此时 JS 引擎已经在doAsync1完成了它所能做的doAsync1 ,所以它跳出该函数并调用下一行doAsync2 同样, doAsync2为将来的执行和返回安排其回调。

接下来,引擎将执行console.log行,使DONE出现在控制台中。

1000 毫秒后,由 doAsync1 调度的回调将运行 execute 并将Async 1 complete到控制台。 doAsync2 1000 毫秒,由doAsync2安排的回调将记录Async 2 complete

基本回调

现在让我们说doAsync1doAsync2生成一些我们想要在main使用的数据,一旦两者都完成。 在 JS 中,我们传统上使用回调来在我们感兴趣的某些操作完成时得到通知。

// Copyright 2019 Google LLC.
// SPDX-License-Identifier: Apache-2.0

function doAsync1(callback) {
  setTimeout(() => {
    console.log('Async 1 started');

    const data = "Async 1 payload";
    callback(data);
  }, 1000);
}

function doAsync2(callback) {
  setTimeout(() => {
    console.log('Async 2 started');

    const data = "Async 2 payload";
    callback(data);
  }, 2000);
}

(function main() {
  const response = {};
  doAsync1(handleAsync1);
  doAsync2(handleAsync2);

  function handleAsync1(data) {
    response.async1 = data;
    handleComplete();
  }
  function handleAsync2(data) {
    response.async2 = data;
    handleComplete();
  }
  function handleComplete() {
    if (response.hasOwnProperty('async1') && response.hasOwnProperty('async2')) {
      console.log('DONE', response);
    }
  }
})();

承诺

虽然这可以完成工作,但它有点冗长。 Promise 是一次性回调的抽象,可以更轻松地将工作块链接在一起。

// Copyright 2019 Google LLC.
// SPDX-License-Identifier: Apache-2.0

// Promisified version of setTimeout
function timeout(duration) {
  return new Promise(resolve => {
    setTimeout(resolve, duration);
  });
}

function doAsync1(callback) {
  return timeout(1000).then(() => {
    console.log('Async 1 started');
    const data = "Async 1 payload";
    return data;
  });
}

function doAsync2(callback) {
  return timeout(2000).then(() => {
    console.log('Async 2 started');
    const data = "Async 2 payload";
    return data;
  });
}

(function main() {
  // Starts both doAsync1 and doAsync2 at the same time. Once both complete, the
  // promise will resolve with both response values.
  Promise.all([
    doAsync1(),
    doAsync2()
  ]).then(response => {
    console.log('DONE', response[0], response[1]);
  });
})();

异步/等待

ES2016 中,我们获得了 2 个新关键字: asyncawait 这些关键字本质上是语法糖,使在 JavaScript 中使用 promise 变得更容易一些。 出于演示目的,让我们看一下转换为 async/await 的 Promises 示例。

// Copyright 2019 Google LLC.
// SPDX-License-Identifier: Apache-2.0

function timeout(duration) {
  return new Promise(resolve => {
    setTimeout(resolve, duration);
  });
}

async function doAsync1(callback) {
  await timeout(1000);
  console.log('Async 1 started');
  const data = "Async 1 payload";
  return data;
}

async function doAsync1(callback) {
  await timeout(2000);
  console.log('Async 2 started');
  const data = "Async 2 payload";
  return data;
}

(async function main() {
  const response = await Promise.all([
    doAsync1(),
    doAsync2()
  ]);
  console.log('DONE', response[0], response[1]);
})();

要更深入地了解异步函数,请查看Async 函数 - Jake Archibald 的承诺友好

使用以下代码片段将 async/await 函数添加到 chrome 扩展。

用法:将以下代码片段放在内容脚本和后台脚本的开头。

/**
 * Usage:
 * let cookies = await asyncfy(chrome.cookies.getAll)({ url })
 * let tabs = await asyncfy(chrome.tabs.query)({active: true, currentWindow: true})
 *
 * @param fn  A function that takes one or more parameters, and the last parameter is a callback which has one or more parameter. The simplest one is chrome.management.getSelf
 * @returns {function(...[*]): Promise<any>}  Return one value if the results array has only one element, else return the whole results array
 */
let asyncfy = fn => (...args) => {
  return new Promise((resolve, reject) => {
    fn(...args, (...results) => {
      let { lastError } = chrome.runtime
      if (typeof lastError !== 'undefined') reject(lastError);
      else results.length == 1 ? resolve(results[0]) : resolve(results);
    });
  });
}


let isObject = function(obj) {
  var type = typeof obj;
  return type === 'function' || type === 'object' && !!obj;
};


// provide async method to all methods which have one callback.
let handler = {
  get: function(target, prop, receiver) {
    let value = target[prop]
    let type = typeof value
    if(type !== 'undefined') { // including null, false
      if( type === 'function') return value.bind(target); // correct the this for the functions, since we've substituted the original object to the proxy object
      return value;
    }

    if(prop.endsWith('Async')){
      let key = prop.replace(/Async$/, '')

      let method=target[key]
      let asyncMethod = asyncfy(method.bind(target));

      return asyncMethod;
    }
  }
}

// proxy every leaf object
let asyncfyObj = handler => obj => Object.getOwnPropertyNames(obj)
  .filter(prop => isObject(obj[prop]))
  .forEach(prop => obj[prop] = new Proxy(obj[prop], handler))

// intercept the getters of all object in chrome member
asyncfyObj(handler)(chrome)
asyncfyObj(handler)(chrome.storage)


// console.log(`active tab: ${JSON.stringify(await getActiveTabAsync())}`)
let getActiveTabAsync = async () => {
  let tabs = await chrome.tabs.queryAsync({active: true, currentWindow: true});
  return (tabs && tabs.length > 0) ? tabs[0] : null;
}

// chrome.storage.local.set({ foo: 'bar' });
// console.log(`foo: ${await getLocalStorageAsync('foo')}`)
let getLocalStorageAsync = async key => ( await chrome.storage.local.getAsync([key]) ) [key];

测试:将以下代码段放在您的后台脚本中,并确保已将相关权限添加到 manifest.json。

(async () => {
console.log(cookies: ${JSON.stringify(await asyncfy(chrome.cookies.getAll)({ url: 'https://www.stackoverflow.com/' }))})

console.log(active tab: ${JSON.stringify(await getActiveTabAsync())})

chrome.storage.local.set({ 'foo': 'bar'});
console.log(storage: ${await getLocalStorageAsync('foo')})

console.log(extension install type: ${( await chrome.management.getSelfAsync() )['installType']})
} )()

我的要点

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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