javascript - 如何使用多个 XMLHttpRequest.responseText 值?

标签 javascript asynchronous callback es6-promise

在 javascript 中,我怎样才能最好地组合多个值,这些值仅作为回调函数的参数获得,最好不添加库依赖项?

例如考虑

function eventHandler() {
    getSomethingAsync(function(something){
        getOtherAsync(function(other){
            console.log([something.value, other.status]);
        });
    });
}

这看起来像 CallbackHell(.com) 的起点.

在其他一些语言中,此时我会使用 promises,按照

function eventHandler() {
    var something = getSomethingPromise();
    var other = getOtherPromise();
    console.log([something.get().value, other.get().status]); 
}

但似乎即使是 ES6-promises 也不允许这样压缩代码,保留冗长(容易出错?)的重复模式,如

Promise.all([somePromise, otherPromise]).then(
    function([some, other]){
        console.log([some.value, other.status]);
    });

我遇到的一个例子是试图将 chrome.tabs.getSelected 提供的选项卡和 XMLHTTPRequest 提供的 responseText 结合起来一个 Chrome 扩展(见下面的“示例代码#2”)。

示例代码 #1(模型)

复制/粘贴到 stackoverflow.com 源内的开发人员控制台。

从不同的(模型)异步函数中获取三个值,并从中构造一个新对象。出于演示目的,结果只是打印到控制台。

// mockup async functions 
function someAsyncAPI(){ 
    var handle = { 'send': function(){ handle.callback({value: 'SOME_VALUE'}); }}
    return handle;
}
function otherAsyncAPI(callback){ 
    callback({ 'version': '1.0', 'value': 'OTHER_VALUE' });
}

function callbackHell(){
    // Issue: 
    //   - One level of nesting for each value. 
    //   - Hard to add more values.
    //   - Doesn't make use of irrelevance of order of evaluation.

    var req = new XMLHttpRequest();
    req.open('GET', '/jobs');
    req.onload = function(){
    var handle = someAsyncAPI();
    handle.callback = function(someValue){
        otherAsyncAPI(function(otherValue){
        console.log({
            mode: 'direct-callback',
            jobs: req.responseText,
            some: someValue.value, 
            other: otherValue.value});
        });
    };
    handle.send();
    };
    req.send();
}

function promiseVersion(){ 
    // Issue: 
    //   - Still seems repetitive, verbose. 
    //   - Promise.all line repeats variable names (likely place of errors?).
    var jobsPromise = new Promise(function(resolve,reject){
    var req = new XMLHttpRequest();
    req.open('GET', '/jobs');
    req.onload = function() { resolve(req.responseText); };
    req.send();
    });
    var somePromise = new Promise(function(resolve,reject){
    var handle = someAsyncAPI();
    handle.callback = resolve;
    handle.send();
    });
    var otherPromise = new Promise(function(resolve,reject){ 
    otherAsyncAPI(resolve); 
    });
    Promise.all([jobsPromise, somePromise, otherPromise])
    .then(function([jobsValue, someValue, otherValue]){
        console.log({
        mode: 'direct-callback',
        jobs: jobsValue,
        some: someValue.value, 
        other: otherValue.value});
    });
}

callbackHell();
promiseVersion();

示例代码 #2(真实示例)

通过重定向到数据 URI 使当前选项卡休眠的 Chrome 扩展。本质上,我对“TabMemFree”等插件的核心思想的看法。

# --- popup.js -----------------------------------------------------
"use strict";

document.getElementById('hibernateTab1').onclick = hibernateTab1;
document.getElementById('hibernateTab2').onclick = hibernateTab2;

function hibernateTab1(tab){
    // Issues: 
    //   - Unintuitive order or statements
    //   - Beginning of nesting-hell

    chrome.tabs.getSelected(null, function(selectedTab){
    var req = new XMLHttpRequest();
    req.open('GET', 'suspended.html');
    req.onload = function(){
        var pagesource = req.responseText
        .replace(/__TITLE__/g, JSON.stringify(selectedTab.title))
        .replace(/__BACKURL__/g, JSON.stringify(selectedTab.url));
        var datauri = "data:text/html;base64," + btoa(pagesource);
        chrome.tabs.update(selectedTab.id, {"url": datauri}); 
    };
    req.send();
    });
}

function hibernateTab2(){ 
    // Improvements:
    //   - Clean separation of independent steps.
    //   - Reduces nesting by one level per independent asynchronous
    //     value after the first.
    //   - Independent values might even be calculated in parallel?
    // Issues: 
    //   - Duplicate variable names in `Promise.all` line are prone to error. 
    //   - Still seems needlessly long,  with a lot of padding.

    var template = new Promise(function(resolve,reject){
        var req = new XMLHttpRequest();
        req.open('GET', 'suspended.html'); 
        req.onload = function(){ resolve(req.responseText); };
        req.send();
    });
    var selectedTab = new Promise(function(resolve,reject){
        chrome.tabs.getSelected(null, resolve);
    });
    Promise.all([template, selectedTab]).then(function([template, selectedTab]){
        var pagesource = template
            .replace(/__TITLE__/g, JSON.stringify(selectedTab.title))
            .replace(/__BACKURL__/g, JSON.stringify(selectedTab.url));
        var datauri = "data:text/html;base64," + btoa(pagesource);
        chrome.tabs.update(selectedTab.id, {"url": datauri});
    });
}

# --- popup.html -----------------------------------------------------
<html>
  <head>
    <title>Silence of the Tabs</title>
  </head>
  <body style='width:300px;'>
    <p><h1>Hello World.</h1></p>
    <p><a href='#' id='hibernateTab1'>hibernateTab1()</a></p>
    <p><a href='#' id='hibernateTab2'>hibernateTab2()</a></p>
  </body>
  <script src='popup.js'></script>
</html>

# --- suspended.html -----------------------------------------------------
<html>
  <body>
    <a id='goback'>Go Back</a>
  </body>
  <script>
    var g = document.getElementById('goback');
    var url=__BACKURL__;
    var title=__TITLE__; 
    document.title=title;
    g.href = 'javascript:goback()';
    g.innerText = title; 
    function goback(){
    if(history.length > 2){
        history.back();
    } else {
        location.href = url;
    }
    }
  </script>
</html>


# --- manifest.json -----------------------------------------------------
{
  "manifest_version": 2,

  "name": "Unload Tab",
  "description": "Unload Tab",
  "version": "0.1",

  "browser_action": {
    "default_popup": "popup.html"
  },
  "permissions": [
    "activeTab"
  ]
}

最佳答案

显然是 async/wait keywords是我要找的。有了这些,我的模型示例可以写成

async function async_await_2(){
    var jobs = new Promise(function(resolve,reject){
        var req = new XMLHttpRequest();
        req.open('GET', '/jobs');
        req.onload = function() { resolve(req.responseText); };
        req.send();
    });
    var some = new Promise(function(resolve,reject){
        var handle = someAsyncAPI();
        handle.callback = resolve;
        handle.send();
    });
    var other = new Promise(function(resolve,reject){ 
        otherAsyncAPI(resolve); 
    });
    console.log({
        mode: 'async-await',
        jobs: await jobs,
        some: (await some).value,
        other: (await other).value});
}

或者(可能导致 promise 的顺序执行而不是并行执行)

async function async_await(){
    // Concise, but sacrifices parallelism of the Promises?
    var jobs = await new Promise(function(resolve,reject){
        var req = new XMLHttpRequest();
        req.open('GET', '/jobs');
        req.onload = function() { resolve(req.responseText); };
        req.send();
    });
    var some = await new Promise(function(resolve,reject){
        var handle = someAsyncAPI();
        handle.callback = resolve;
        handle.send();
    });
    var other = await new Promise(function(resolve,reject){ 
        otherAsyncAPI(resolve); 
    });
    console.log({
        mode: 'async-await',
        jobs: jobs,
        some: some.value,
        other: other.value});
}

关于javascript - 如何使用多个 XMLHttpRequest.responseText 值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45284209/

相关文章:

javascript - 已保存的 javascript facebook API 响应仍未定义?

javascript - 通过 Window.open 的 Web Intent 回调事件只是没有发生

javascript - d3.max() 不返回数组中的最大值?

javascript - cfform 目标 iframe 页面丢失

javascript - promise 不等待异步对象条目 firebase 调用

javascript - Node.js,等待所有 Redis 查询完成后再继续执行

javascript - 使用javascript在iframe src中添加动态html模板

javascript - NodeJS 和 Express - 分离我的 Controller 和模型

javascript - DOM 查找是阻塞的还是异步的?

ruby-on-rails - 如何修复此 before_destroy 回调 [rails]