1

我需要从头开始写这个asyncMap函数。我想我已经差不多了,但我不确定为什么我一直得到错误的答案。这里是我到目前为止的代码:如何从头开始在Javascript中编写异步Map函数?

function wait3For1(callback){ 
    setTimeout(function(){ 
     callback('one') 
    }, 300) 
} 

function wait2For5(callback){ 
    setTimeout(function(){ 
     callback('five') 
    }, 200) 
} 


function asyncMap(tasks, callback){ 
    return callback(
    tasks.map((item) => 
     item((element) => element))) 
} 

asyncMap([wait3For1, wait2For5], function(arr){ 
    console.log(arr) //expect ['one', 'five'] 
}); 

我不断收到[undefined, undefined] 我敢肯定那是因为我没有做回调wait2For5和wait3For1正确的,但不知道是什么问题。

在此先感谢!

+0

这是一个完美的承诺使用情况,它比使用回调有更好的错误捕获 – charlietfl

+0

您的asyncMap是否假设平行或按顺序做事情?换句话说,所有异步操作可以同时在运行吗?还是您希望它们能够一个接一个执行? – jfriend00

回答

1

问题是,您不会等待结果回来,收集它们,然后通过回调将它们发送回去。看看这段代码是否有帮助。 (它的工作原理当你的程序进行测试。)

function asyncMap(tasks, callback) { 
    // array to collect the results 
    let results = []; 

    // count of how many results we're waiting for 
    let remaining = tasks.length; 

    tasks.forEach((task, i) => { 
     task((result) => { 
      // Store the result in the right position. 
      results[i] = result; 

      // See how many results we're still waiting for. 
      remaining -= 1; 

      // If we're done, invoke the callback. 
      if (remaining === 0) { 
       callback(results); 
      } 
     }); 
    }); 
} 
+0

如果不打算使用映射函数,函数应该被称为“asyncMap”以外的内容。 – naomik

+1

@naomik同意。 – smarx

0

你基本上是建立穷人的希望,但是没有任何错误处理能力。

尝试

function waitFor(val, dur){ 
 
     return new Promise(function(resolve, reject) {  
 
     setTimeout(function() { 
 
      resolve(val) 
 
     }, dur); 
 
     });  
 
    } 
 

 
    
 
    Promise.all([waitFor('one',600), waitFor('five', 100)]).then(function(arr) { 
 
     console.log(arr) //expect ['one', 'five'] 
 
    }).catch(function(err){ 
 
     console.log('ooops error:' , err) 
 
    });

1

在代码中,您使用的同步Array.prototype.map

function asyncMap(tasks, callback){ 
    return callback(
    tasks.map((item) => 
     item((element) => element))) 
} 

由于wait3For1wait2For5没有return,他们会隐返回undefined这将被用在的结果中打电话给。很明显,我们希望在将映射值分配给最终结果之前等待回调被调用。

的另一个问题是,映射在一个阵列使用功能map(items)并没有真正意义的无功能对项目进行映射。所以我们也会在下面的解决方案中解决这个问题。

如果您以asyncReduce开头,然后将asyncMap作为异步缩减开始,它将有所帮助。请注意,下面的代码将处理系列中的项目。如果您希望并行处理项目,则需要稍微不同的方法。让我知道在评论中,我会很乐意写出另一个变体。

function wait3For1(callback){ 
 
    setTimeout(function(){ 
 
    callback('one') 
 
    }, 300) 
 
} 
 

 
function wait2For5(callback){ 
 
    setTimeout(function(){ 
 
    callback('five') 
 
    }, 200) 
 
} 
 

 
function asyncReduce(xs, f, initial, callback) { 
 
    if (xs.length === 0) 
 
    callback(null, initial) 
 
    else 
 
    f(initial, xs[0], function(x) { 
 
     asyncReduce(xs.slice(1), f, x, callback) 
 
    }) 
 
} 
 

 
function asyncMap(xs, f, callback) { 
 
    asyncReduce(xs, function(acc, x, k) { 
 
    f(x, function(y) { k(acc.concat([y])) }) 
 
    }, [], callback) 
 
} 
 

 
asyncMap([wait3For1, wait2For5], function(f,callback) { 
 
    f(callback) 
 
}, function(err, arr) { 
 
    console.log(arr) //=> ['one', 'five'] 
 
})

继续运行的代码片段,看看它的工作


我你使用一些箭头的功能看,所以你可能有一个稍微近点的版本。这里是完全相同的,但使用ES6代替

// ES6 
 
const wait3For1 = callback=> 
 
    setTimeout(callback, 300, 'one') 
 

 
const wait2For5 = callback=> 
 
    setTimeout(callback, 200, 'five') 
 

 
const asyncReduce = ([x,...xs], f, initial, callback) => 
 
    x === undefined 
 
    ? callback(null, initial) 
 
    : f(initial, x, y=> asyncReduce(xs, f, y, callback)) 
 

 
const asyncMap = (xs, f, callback)=> 
 
    asyncReduce(xs, (acc, x, k)=> 
 
    f(x, y=> k([...acc, y])) 
 
    , [], callback) 
 

 
asyncMap(
 
    [wait3For1, wait2For5], 
 
    (f, callback)=> f(callback), 
 
    (err, arr)=> console.log(arr) // ['one', 'five'] 
 
)


从头手工艺高阶函数是一个非常有价值的锻炼,但作为另一个答案指出,你只是实施“穷人的Promise.all”,但多用途少得多。您应该将wait3For1wait2For5转换为Promise创作者,并使用Promise.all代替。

你的下一个问题可能是如何实施Promise.all ......我已经完成了这个最近,发现它是一个非常有趣的挑战。我希望在这个答案中共享的一些技巧将有助于您选择从头开始探索一个实现^ _^