在 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/