我必须编写一个javaScript函数,将一些数据返回给调用者。
在该功能中,我有多种方法来检索数据,即
每个选项可能需要自己的时间才能完成,可能会成功或失败。
我想要做的是,异步/并行执行所有这三个选项,并返回先返回的结果。
我理解JavaScript中不可能并行执行,因为它是单线程的,但我想至少异步执行它们并取消其他任务,如果其中一个返回成功结果。
我还有一个问题。
提前返回并继续执行JavaScript函数中的剩余任务。
伪代码示例:
function getOrder(id) {
var order;
// early return if the order is found in cache.
if (order = cache.get(id)) return order;
// continue to get the order from the backend REST API.
order = cache.put(backend.get(id));
return order;
}
请建议如何在JavaScript中实现这些要求。
参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Promise.race(可迭代)
返回在iterable中的第一个promise解析时解析的promise。
var p1 = new Promise(function(resolve, reject) { setTimeout(resolve, 500, "one"); });
var p2 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "two"); });
Promise.race([p1, p2]).then(function(value) {
// value == "two"
});
参考:http://gpars.org/1.1.0/guide/guide/single.html
import groovyx.gpars.dataflow.Promise
import groovyx.gpars.dataflow.Select
import groovyx.gpars.group.DefaultPGroup
import java.util.concurrent.atomic.AtomicBoolean
/**
* Demonstrates the use of dataflow tasks and selects to pick the fastest result of concurrently run calculations.
* It shows a waz to cancel the slower tasks once a result is known
*/
final group = new DefaultPGroup()
final done = new AtomicBoolean()
group.with {
Promise p1 = task {
sleep(1000)
if (done.get()) return
10 * 10 + 1
}
Promise p2 = task {
sleep(1000)
if (done.get()) return
5 * 20 + 2
}
Promise p3 = task {
sleep(1000)
if (done.get()) return
1 * 100 + 3
}
final alt = new Select(group, p1, p2, p3, Select.createTimeout(500))
def result = alt.select()
done.set(true)
println "Result: " + result
}
早期返回和互动功能
angular.module('org.common')
.service('SpaceService', function ($q, $timeout, Restangular, $angularCacheFactory) {
var _spacesCache = $angularCacheFactory('spacesCache', {
maxAge: 120000, // items expire after two min
deleteOnExpire: 'aggressive',
onExpire: function (key, value) {
Restangular.one('organizations', key).getList('spaces').then(function (data) {
_spacesCache.put(key, data);
});
}
});
/**
* @class SpaceService
*/
return {
getAllSpaces: function (orgId) {
var deferred = $q.defer();
var spaces;
if (spaces = _spacesCache.get(orgId)) {
deferred.resolve(spaces);
} else {
Restangular.one('organizations', orgId).getList('spaces').then(function (data) {
_spacesCache.put(orgId, data);
deferred.resolve(data);
} , function errorCallback(err) {
deferred.reject(err);
});
}
return deferred.promise;
},
getAllSpaces1: function (orgId) {
var deferred = $q.defer();
var spaces;
var timerID = $timeout(
Restangular.one('organizations', orgId).getList('spaces').then(function (data) {
_spacesCache.put(orgId, data);
deferred.resolve(data);
}), function errorCallback(err) {
deferred.reject(err);
}, 0);
deferred.notify('Trying the cache now...'); //progress notification
if (spaces = _spacesCache.get(orgId)) {
$timeout.cancel(timerID);
deferred.resolve(spaces);
}
return deferred.promise;
},
getAllSpaces2: function (orgId) {
// set up a dummy canceler
var canceler = $q.defer();
var deferred = $q.defer();
var spaces;
$timeout(
Restangular.one('organizations', orgId).withHttpConfig({timeout: canceler.promise}).getList('spaces').then(function (data) {
_spacesCache.put(orgId, data);
deferred.resolve(data);
}), function errorCallback(err) {
deferred.reject(err);
}, 0);
if (spaces = _spacesCache.get(orgId)) {
canceler.resolve();
deferred.resolve(spaces);
}
return deferred.promise;
},
addSpace: function (orgId, space) {
_spacesCache.remove(orgId);
// do something with the data
return '';
},
editSpace: function (space) {
_spacesCache.remove(space.organization.id);
// do something with the data
return '';
},
deleteSpace: function (space) {
console.table(space);
_spacesCache.remove(space.organization.id);
return space.remove();
}
};
});
答案 0 :(得分:7)
就个人而言,我会按顺序尝试三次异步检索,从最便宜的开始到最昂贵的结尾。但是,响应三个并行检索中的第一个是一个有趣的问题。
您应该能够利用$q.all(promises)
的特征,其中:
但是你想要颠倒逻辑:
这应该可以通过invert()
实用程序实现,该实用程序将成功转换为失败,反之亦然。
function invert(promise) {
return promise.then(function(x) {
return $q.defer().reject(x).promise;
}, function(x) {
return $q.defer().resolve(x).promise;
});
}
和first()
实用程序,以提供所需的行为:
function first(arr) {
return invert($q.all(arr.map(invert)));
}
注意:
arr
是一系列承诺array.map()
的本机实现(否则您可以显式循环以实现相同的效果)invert()
中的first()
外部恢复了对其返回的承诺的正确感觉然后getOrder()
将是这样的:
function getOrder(id) {
return first([
cache.get(id),
localStorage.get(id).then(cache.put),
backend.get(id).then(cache.put).then(localStorage.put)
]);
}
因此,getOrder(id)
应该返回订单的承诺(而不是直接订单)。
答案 1 :(得分:0)
在api调用中创建一个广播事件,然后创建$ scope。$ on来监听那些广播,当$ on get激活时,执行刷新这些对象的功能。
因此,在您的服务中有一个函数可以对您的其他api进行ajax调用。 你将有3个ajax调用。和3个听众。他们每个人都会看起来像这样。
这只是sudo代码,但这种格式就是你这样做的
之类的东西 $http({
method: "GET",
url: url_of_api,
}).success(function(data, *args, *kwargs){
$rooteScope.$braodcast('success', data)
})
在你的控制器中有一个像这样的听众
$scope.$on('success', function(event, args){
// Check the state of the other objects, have they been refreshed - you probably want to set flags to check
if (No Flags are Set):
$scope.data = args // which would be the returned data adn $scope.data would be what you're trying to refresh.
}
答案 2 :(得分:0)
示例getOrder
中的问题在于,如果3个查找函数将是异步的,则不会立即从它们那里获取订单,因为它们没有阻塞,{{1将返回getOrder
;你最好定义一个回调函数,它对第一个返回的null
数据采取行动,然后忽略其余的数据。
order
使您的数据查找功能接受回调
var doSomethingWithTheOrder = function CallBackOnce (yourResult) {
if (!CallBackOnce.returned) {
CallBackOnce.returned = true;
// Handle the returned data
console.log('handle', yourResult);
} else {
// Ignore the rest
console.log('you are too late');
}
}
并将回调函数传递给每个
function cacheLookUp(id, callback) {
// Make a real lookup here
setTimeout(function () {
callback('order data from cache');
}, 3000);
}
function localeStorageLookUp(id, callback) {
// Make a real lookup here
setTimeout(function () {
callback('order data from locale storage');
}, 1500);
}
function restLookUp(id, callback) {
// Make a real lookup here
setTimeout(function () {
callback('order data from rest');
}, 5000);
}