使用生成器异步调用列表中的每一项
Asynchronous call for every item of list with generator
我不知道如何恰当地写这个问题的标题,但我会解释我的问题:
假设有一个包含一些数据的列表,例如:["first", "second", "third"]
还有一个 AJAX 调用对其参数做一些事情,例如:
function ajax(data){
return new Promise(function (resolve, reject) {
setTimeout(() => resolve(data+"1"), 2000)
});
}
对于每个 AJAX 调用,您都需要一个 follow-up 操作,例如:
ajax(e).done(data => d.resolve(data+"2"));
现在我想对列表中的每个项目异步进行 AJAX 调用和 follow-up 操作,但想等待(非阻塞)直到每个项目完成.
对于解决方案,我想使用生成器和 co library。
只有 运行 每个列表项的 AJAX 异步调用效果很好:
var consoleLine = "<p class=\"console-line\"></p>";
console = {
log: function (text) {
$("#console-log").append($(consoleLine).html(text));
}
};
co(function*(){
let res = yield ["first", "second", "third"].map(e => ajax(e));
res.forEach((a, b, c) => console.log(a));
});
function ajax(data){
return new Promise(function (resolve, reject) {
setTimeout(() => resolve(data+"1"), 2000)
});
}
.console-line
{
font-family: monospace;
margin: 2px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://raw.githubusercontent.com/tj/co/master/index.js"></script>
<div id="console-log"></div>
但是 运行 它与 follow-up 操作不起作用:
var consoleLine = "<p class=\"console-line\"></p>";
console = {
log: function (text) {
$("#console-log").append($(consoleLine).html(text));
}
};
co(function*(){
let res = yield test(["first", "second", "third"]);
res.forEach((a, b, c) => console.log(a));
});
function test(g) {
return g.map(e => function(){
let d = new $.Deferred();
ajax(e).done(data => d.resolve(data+"2"));
return d.promise();
});
}
function ajax(data){
return new Promise(function (resolve, reject) {
setTimeout(() => resolve(data+"1"), 2000)
});
}
.console-line
{
font-family: monospace;
margin: 2px;
}
<script src="https://raw.githubusercontent.com/tj/co/master/index.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="console-log"></div>
这是为什么?如何让我的需求生效?
Now i want to make the AJAX call and the follow-up action asynchronously for every item of the list but want to wait (non blocking) until every item is finished.
听起来您想等待所有的承诺兑现。这就是 Promise.all() and $.when 的好处。
var data = ["first", "second", "third"];
function ajax(data){
let d = new $.Deferred();
setTimeout(() => d.resolve(data+"1"), 1000);
return d.promise();
}
function followUpAction(data){
console.log('following', data);
}
var promises = data.map(function(item) {
return ajax(item).then(followUpAction);
});
$.when.apply($, promises).then(function() {
console.log('all done');
});
我想使用发电机,因此 rodneyrehm 的回答不能解决我的需要。但他的回答确实帮助我解决了我的问题!
我现在不再自己创建和 return 一个新的 promise,而是使用 .then()
本身 return 一个 promise(自 jQuery 1.8) .
这就是我需要更改以使其正常工作的全部内容:
旧:
function test(g) {
return g.map(e => function(){
let d = new $.Deferred();
ajax(e).done(data => d.resolve(data+"2"));
return d.promise();
});
}
新:
function test(g) {
return g.map(e => ajax(e).then(data => data+"2"));
}
最终的解决方案看起来是这样的:
var consoleLine = "<p class=\"console-line\"></p>";
console = {
log: function (text) {
$("#console-log").append($(consoleLine).html(text));
}
};
var list = ["first", "second", "third"];
co(function*(){
let res = yield list.map(e => ajax(e).then(r => r+"2"));
res.forEach((a, b, c) => console.log(a));
});
function ajax(data){
return new Promise(function (resolve, reject) {
setTimeout(() => resolve(data+"1"), 2000)
});
}
.console-line
{
font-family: monospace;
margin: 2px;
}
<script src="https://raw.githubusercontent.com/tj/co/master/index.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="console-log"></div>
感谢 rodneyrehm 指出我的解决方案。
这是你的问题:
return g.map(e => function(){
// ^^ ^^^^^^^^^^
let d = new $.Deferred();
ajax(e).done(data => d.resolve(data+"2"));
return d.promise();
});
这是一个返回函数表达式的箭头函数。你要么想要
return g.map(function(e) {
或
return g.map(e => {
让它工作,否则你只会得到一个函数数组(co
会以奇怪的方式处理它)。
是的,you definitely should use then
而不是 done
+ deferreds。
我不喜欢使用带有承诺的生成器。当 ES7 的 async
await
出现时,我会重新考虑这一点。我相信这项工作可以通过使用 Array.prototype.reduce()
以功能方式很好地完成,如果它们依赖于它们在数组中的顺序并且需要一个接一个 运行。否则我会推荐 Array.prototype.map()
和 Promise.all
.
的组合
假设错误首先使用回调。
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
function async(data, callback){
data.val+= " msec" // do something with the data asynchronously
setTimeout(_ => callback(false,data.val),data.dur); // we are expected to handle returned data in our callback
}
function myCallback(result){ // supposed to work in the asych function but now moved to then stage
console.log(result);
return result;
}
var dataSet = [100, 200, 300],
promisifiedAsych = promisify(async),
chainedPromises = dataSet.reduce((prom,data) => prom.then(_ => promisifiedAsych({val:data, dur:data*10}))
.then(myCallback), Promise.resolve());
chainedPromises.then(val => console.log("dataSet completed and returned", val))
我不知道如何恰当地写这个问题的标题,但我会解释我的问题:
假设有一个包含一些数据的列表,例如:["first", "second", "third"]
还有一个 AJAX 调用对其参数做一些事情,例如:
function ajax(data){
return new Promise(function (resolve, reject) {
setTimeout(() => resolve(data+"1"), 2000)
});
}
对于每个 AJAX 调用,您都需要一个 follow-up 操作,例如:
ajax(e).done(data => d.resolve(data+"2"));
现在我想对列表中的每个项目异步进行 AJAX 调用和 follow-up 操作,但想等待(非阻塞)直到每个项目完成.
对于解决方案,我想使用生成器和 co library。
只有 运行 每个列表项的 AJAX 异步调用效果很好:
var consoleLine = "<p class=\"console-line\"></p>";
console = {
log: function (text) {
$("#console-log").append($(consoleLine).html(text));
}
};
co(function*(){
let res = yield ["first", "second", "third"].map(e => ajax(e));
res.forEach((a, b, c) => console.log(a));
});
function ajax(data){
return new Promise(function (resolve, reject) {
setTimeout(() => resolve(data+"1"), 2000)
});
}
.console-line
{
font-family: monospace;
margin: 2px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://raw.githubusercontent.com/tj/co/master/index.js"></script>
<div id="console-log"></div>
但是 运行 它与 follow-up 操作不起作用:
var consoleLine = "<p class=\"console-line\"></p>";
console = {
log: function (text) {
$("#console-log").append($(consoleLine).html(text));
}
};
co(function*(){
let res = yield test(["first", "second", "third"]);
res.forEach((a, b, c) => console.log(a));
});
function test(g) {
return g.map(e => function(){
let d = new $.Deferred();
ajax(e).done(data => d.resolve(data+"2"));
return d.promise();
});
}
function ajax(data){
return new Promise(function (resolve, reject) {
setTimeout(() => resolve(data+"1"), 2000)
});
}
.console-line
{
font-family: monospace;
margin: 2px;
}
<script src="https://raw.githubusercontent.com/tj/co/master/index.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="console-log"></div>
这是为什么?如何让我的需求生效?
Now i want to make the AJAX call and the follow-up action asynchronously for every item of the list but want to wait (non blocking) until every item is finished.
听起来您想等待所有的承诺兑现。这就是 Promise.all() and $.when 的好处。
var data = ["first", "second", "third"];
function ajax(data){
let d = new $.Deferred();
setTimeout(() => d.resolve(data+"1"), 1000);
return d.promise();
}
function followUpAction(data){
console.log('following', data);
}
var promises = data.map(function(item) {
return ajax(item).then(followUpAction);
});
$.when.apply($, promises).then(function() {
console.log('all done');
});
我想使用发电机,因此 rodneyrehm 的回答不能解决我的需要。但他的回答确实帮助我解决了我的问题!
我现在不再自己创建和 return 一个新的 promise,而是使用 .then()
本身 return 一个 promise(自 jQuery 1.8) .
这就是我需要更改以使其正常工作的全部内容:
旧:
function test(g) {
return g.map(e => function(){
let d = new $.Deferred();
ajax(e).done(data => d.resolve(data+"2"));
return d.promise();
});
}
新:
function test(g) {
return g.map(e => ajax(e).then(data => data+"2"));
}
最终的解决方案看起来是这样的:
var consoleLine = "<p class=\"console-line\"></p>";
console = {
log: function (text) {
$("#console-log").append($(consoleLine).html(text));
}
};
var list = ["first", "second", "third"];
co(function*(){
let res = yield list.map(e => ajax(e).then(r => r+"2"));
res.forEach((a, b, c) => console.log(a));
});
function ajax(data){
return new Promise(function (resolve, reject) {
setTimeout(() => resolve(data+"1"), 2000)
});
}
.console-line
{
font-family: monospace;
margin: 2px;
}
<script src="https://raw.githubusercontent.com/tj/co/master/index.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="console-log"></div>
感谢 rodneyrehm 指出我的解决方案。
这是你的问题:
return g.map(e => function(){
// ^^ ^^^^^^^^^^
let d = new $.Deferred();
ajax(e).done(data => d.resolve(data+"2"));
return d.promise();
});
这是一个返回函数表达式的箭头函数。你要么想要
return g.map(function(e) {
或
return g.map(e => {
让它工作,否则你只会得到一个函数数组(co
会以奇怪的方式处理它)。
是的,you definitely should use then
而不是 done
+ deferreds。
我不喜欢使用带有承诺的生成器。当 ES7 的 async
await
出现时,我会重新考虑这一点。我相信这项工作可以通过使用 Array.prototype.reduce()
以功能方式很好地完成,如果它们依赖于它们在数组中的顺序并且需要一个接一个 运行。否则我会推荐 Array.prototype.map()
和 Promise.all
.
假设错误首先使用回调。
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
function async(data, callback){
data.val+= " msec" // do something with the data asynchronously
setTimeout(_ => callback(false,data.val),data.dur); // we are expected to handle returned data in our callback
}
function myCallback(result){ // supposed to work in the asych function but now moved to then stage
console.log(result);
return result;
}
var dataSet = [100, 200, 300],
promisifiedAsych = promisify(async),
chainedPromises = dataSet.reduce((prom,data) => prom.then(_ => promisifiedAsych({val:data, dur:data*10}))
.then(myCallback), Promise.resolve());
chainedPromises.then(val => console.log("dataSet completed and returned", val))