我有一个网页,其中不同部分都需要相同的后端数据。每个都是隔离的,因此它们最终最终会对后端进行相同的调用。
当一台服务器正在进行并且由同一网页上的不同代码启动时,避免调用Web服务器的最佳方法是什么?
这是一个例子。我将使用setTimeout来模拟异步调用。
我们假设有一个异步函数返回联系人列表,在这个例子中基本上是一个简单的字符串数组:
var getContacts = function() {
log('Calling back-end to get contact list.');
return new Promise(function(resolve, reject) {
setTimeout(function() {
log('New data received from back-end.');
resolve(["Mary","Frank","Klaus"]);
}, 3000);
});
};
现在,让我们创建三个独立的函数,每个函数为不同的目的调用上述函数。
转出联系人列表:
var dumpContacts = function() {
getContacts().then(function(contacts) {
for( var i = 0; i < contacts.length; i++ ) {
log( "Contact " + (i + 1) + ": " + contacts[i] );
}
});
};
确定特定联系人是否在列表中:
var contactExists = function(contactName) {
return getContacts().then(function(contacts) {
return contacts.indexOf(contactName) >= 0 ? true : false;
});
};
获取第一个联系人的姓名:
var getFirstContact = function() {
return getContacts().then(function(contacts) {
if ( contacts.length > 0 ) {
return contacts[0];
}
});
};
以下是一些使用这三个函数的示例代码:
// Show all contacts
dumpContacts();
// Does contact 'Jane' exist?
contactExists("Jane").then(function(exists){
log("Contact 'Jane' exist: " + exists);
});
getFirstContact().then(function(firstContact){
log("first contact: " + firstContact);
});
以上例程使用全局log()函数。可以使用console.log()代替。上面的log()函数登录到浏览器窗口,实现如下:
function log() {
var args = Array.prototype.slice.call(arguments).join(", ");
console.log(args);
var output = document.getElementById('output');
output.innerHTML += args + "<br/>";
}
并在html中要求以下内容:
<div id='output'><br/></div>
运行上述代码后,您将看到:
Calling back-end to get contact list.
和
New data received from back-end.
三次,这是不必要的。
如何解决这个问题?
这个样本在Plunker上可以执行: http://plnkr.co/edit/6ysbNTf1lSf5b7L3sJxQ?p=preview
答案 0 :(得分:2)
如果希望减少对后端的不必要呼叫的数量,那么请坚持承诺,当它仍未解决时,将其返回给新呼叫,而不是向后面发出另一个呼叫末端。
这是一个例程,它将异步函数(一个返回一个promise)转换为一个只在promise未被解析时才被调用的函数。
var makeThrottleFunction = function (asyncFunction) {
var currentPromiser = getPromise = function() {
var promise = new Promise(function(resolve, reject) {
asyncFunction().then(function(value) {
resolve(value);
currentPromiser = getPromise;
}).catch(function(e) {
reject(e);
currentPromiser = getPromise;
});
});
currentPromiser = function() {
return promise;
};
return promise;
}
return function () {
return currentPromiser();
};
};
在您的日常工作中,您可以像这样转换getContacts
:
var getContacts = makeThrottleFunction(getContacts);
或直接传递整个函数体。
请记住,这只适用于后端的无参数调用。
示例plunker代码:http://plnkr.co/edit/4JTtHmFTZmiHugWNnlo9?p=preview
答案 1 :(得分:2)
只需将结果缓存在进行调用的函数中:
function cache(promiseReturningFn){
var cachedVal = null; // start without cached value
function cached(){
if(cachedVal) return cachedVal; // prefer cached result
cachedVal = promiseReturningFn.apply(this, arguments); // delegate
return cachedVal; // after we saved it, return it
}
cached.flush = function(){ cachedVal = undefined; };
return cached;
}
这有一个失败的警告,实际结果是null,但否则它可以很好地完成工作。
你现在可以缓存任何promise返回函数 - 上面的版本只缓存忽略参数 - 但是你也可以构建一个类似的基于不同参数的Map和缓存 - 但是让我们关注你的用例。
var getContactsCached = cache(getContacts);
getContactsCached();
getContactsCached();
getContactsCached(); // only one async call ever made
缓存方法实际上甚至与promises无关 - 它只需要一个函数并缓存其结果 - 你可以将它用于任何事情。事实上,如果您使用像下划线这样的库,则可以使用_.memoize
为您完成此操作。
答案 2 :(得分:-4)
修改,更新
删除&#34;嵌套&#34; ternary
模式;添加
dfd.err()
,.catch()
来处理传递给Promise.reject(/* reason ? */)
的{{1}} arguments
; dfd.fn()
中的args === ""
来处理dfd.process()
:空""
作为String
传递给argument
dfd.fn()
要求.then()
原住民then.apply(dfd.promise, [contactExists, getFirstContact])
作为Error()
传递:argument
在dfd.fn(new Error("error"))
范围处理; global
仍会返回dfd.fn()
。可以在dfd.promise
之前或之前进行调整,以及早期&#34;在dfd.process()
或将Error
传递给Error
;根据要求。未在dfd.err()
以下处理。
尝试
js
var dfd = {
// set `active` : `false`
"active": false,
// set `promise`: `undefined`
"promise": void 0,
// process `arguments`, if any, passed to `dfd.fn`
"process": function process(args) {
// return `Function` call, `arguments`,
// or "current" `dfd.promise`;
// were `args`:`arguments` passed ?
// handle `""` empty `String` passed as `args`
return args === "" || !!args
// if `args`:`Function`, call `args` with `this`:`dfd`,
// or, set `args` as `value`, `reason`
// of "next" `dfd.promise`
// return "next" `dfd.promise`
? args instanceof Function && args.call(this) || args
// set `dfd.active`:`false`
// when "current" `dfd.promise`:`Promise` `fulfilled`,
// return "current" `dfd.promise`
: this.active = true && this.promise
},
// handle `fulfilled` `Promise.reject(/* `reason` ? */)`,
// passed as `args` to `dfd.fn`
"err": function err(e) {
// notify , log `reason`:`Promise.reject(/* `reason` ? */)`, if any,
// or, log `undefined` , if no `reason` passed: `Promise.reject()`
console.log("rejected `Promise` reason:", e || void 0);
},
// do stuff
"fn": function fn(args /* , do other stuff */) {
// set `_dfd` : `this` : `dfd` object
var _dfd = this;
// if "current" `dfd.promise`:`Promise` processing,
// wait for `fulfilled` `dfd.promise`;
// return `dfd.promise`
_dfd.promise = !_dfd.active
// set, reset `dfd.promise`
// process call to `dfd.async`;
// `args`:`arguments` passed to `dfd.fn` ?,
// if `args` passed, are `args` `function` ?,
// if `args` `function`, call `args` with
// `this`:`dfd`;
// or, return `args`
? _dfd.process(args)
// if `_dfd.active`, `_dfd.promise` defined,
// return "current" `_dfd.promise`
: _dfd.promise.then(function(deferred) {
// `deferred`:`_dfd.promise`
// do stuff with `deferred`,
// do other stuff,
// return "current", "next" `deferred`
return deferred
})
// handle `args`:`fulfilled`,
// `Promise.reject(/* `reason` ? */)`
.catch(_dfd.err);
return Promise.resolve(_dfd.promise).then(function(data) {
// `data`:`undefined`, `_dfd.promise`
// set `_dfd.active`:`false`,
// return `value` of "current", "next" `_dfd.promise`
_dfd.active = false;
return data
})
// handle `fulfilled` `Promise.reject(/* `reason` ? */),
// if reaches here ?
.catch(_dfd.err)
}
};
function log() {
var args = Array.prototype.slice.call(arguments).join(", ");
console.log(args);
var output = document.getElementById('output');
output.innerHTML += args + "<br/>";
};
var dumpContacts = function () {
log('Calling back-end to get contact list.');
return new Promise(function (resolve, reject) {
setTimeout(function () {
log('New data received from back-end.');
resolve(["Mary", "Frank", "Klaus"]);
}, 3000);
});
};
var getContacts = function () {
return dfd.async().then(function (contacts) {
for (var i = 0; i < contacts.length; i++) {
log("Contact " + (i + 1) + ": " + contacts[i]);
}
});
};
var contactExists = function (contactName) {
return dfd.async().then(function (contacts) {
return contacts.indexOf(contactName) >= 0 ? true : false;
});
};
var getFirstContact = function () {
return dfd.async().then(function (contacts) {
if (contacts.length > 0) {
return contacts[0];
}
return contacts
});
};
// Test:
// Show all contacts
dfd.async(dumpContacts)
.then(getContacts)
.then.apply(dfd.promise, [
// Does contact 'Jane' exist?
contactExists("Jane").then(function (exists) {
log("Contact 'Jane' exist: " + exists);
})
, getFirstContact().then(function (firstContact) {
log("first contact: " + firstContact);
})
]);
&#13;
function log() {
var args = Array.prototype.slice.call(arguments).join(", ");
console.log(args);
var output = document.getElementById('output');
output.innerHTML += args + "<br/>";
return output
};
var dfd = {
"active": false,
"promise": void 0,
"process": function process(args) {
return args === "" || !!args
? args instanceof Function && args.call(this) || args
: this.active = true && this.promise
},
"err": function err(e) {
console.log("rejected `Promise` reason:", e || void 0);
},
"fn": function fn(args) {
var _dfd = this;
_dfd.promise = !_dfd.active
? _dfd.process(args)
: _dfd.promise.then(function(deferred) {
return deferred
})
.catch(_dfd.err);
return Promise.resolve(_dfd.promise).then(function(data) {
_dfd.active = false;
return data
})
.catch(_dfd.err)
}
};
var dumpContacts = function() {
log('Calling back-end to get contact list.');
return new Promise(function(resolve, reject) {
setTimeout(function() {
log('New data received from back-end.');
resolve(["Mary", "Frank", "Klaus"]);
}, 3000);
});
};
var getContacts = function() {
return dfd.fn().then(function(contacts) {
for (var i = 0; i < contacts.length; i++) {
log("Contact " + (i + 1) + ": " + contacts[i]);
}
});
};
var contactExists = function(contactName) {
return dfd.fn().then(function(contacts) {
return contacts.indexOf(contactName) >= 0 ? true : false;
});
};
var getFirstContact = function() {
return dfd.fn().then(function(contacts) {
if (contacts.length > 0) {
return contacts[0];
}
return contacts
});
};
// Test:
// Show all contacts
dfd.fn(dumpContacts)
.then(getContacts)
.then(function() {
// Does contact 'Jane' exist?
return contactExists("Jane").then(function(exists) {
log("Contact 'Jane' exist: " + exists);
})
})
.then(function() {
return getFirstContact().then(function(firstContact) {
log("first contact: " + firstContact);
})
});
&#13;