如何使用多个XMLHttpRequest.responseText值?

时间:2017-07-24 15:14:18

标签: javascript asynchronous callback es6-promise

在javascript中,我怎样才能最好地将多个值作为参数组合到回调函数中,最好不要添加库依赖项?

E.g。考虑

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承诺也不会允许这样的代码通缩,保留冗长(容易出错?)的重复模式,如

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

我遇到的一个示例是尝试将chrome.tabs.getSelected提供的标签与responseText提供的XMLHTTPRequest组合在Chrome扩展程序中(请参阅"示例代码#2& #34;下面)。

示例代码#1(模型)

复制/粘贴到stackoverflow.com origin中的开发者控制台中。

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

// 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(真实示例)

Chrome扩展程序通过重定向到数据URI来休眠当前选项卡。基本上我对插件的核心理念如" 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"
  ]
}

1 个答案:

答案 0 :(得分:1)

显然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});
}

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

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});
}