在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;下面)。
复制/粘贴到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();
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"
]
}
答案 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});
}