2016-09-14 63 views
4

在我的節點應用程序中,我需要產生多個文件寫入並等待它們完成,然後再繼續處理其他內容。我已經通過以下方式實現了這一點:JS:優雅的方式來等待回調完成

let counter = 0; 
(some loop declaration) { 
    // (preparing data etc) 
    counter += 1; 
    fs.writeFile(fname, fdata, (err) => { 
     counter -= 1; 
    }); 
} 
let waitForCallbacks = function() { 
    if (fcounter > 0) { 
     setTimeout(waitForCallbacks, 0); 
    } 
}; 
waitForCallbacks(); 

雖然它按需運作,但我覺得可能有一些更好的習語。有什麼建議麼?

+9

承諾和'Promise.all' – zerkms

+2

常量writeFileP =需要( '藍鳥')。promisify(FS。 writeFile); // writeFileP(...)返回一個承諾 – brainjam

回答

4

儘管它按照我們的期望工作,但我覺得可能有一些更好的習語。

這是承諾的目的之一。下面是與承諾改寫代碼(故能走的更遠,還有庫諾 - IFY的NodeJS的API):

let operations = [] 
(some loop declaration) { 
    // (preparing data etc) 
    operations.push(new Promise((resolve, reject) => { 
     fs.writeFile(fname, fdata, (err) => { 
      if (err) { 
       reject(err); 
      } else { 
       resolve(); 
      } 
     }); 
    })); 
} 
Promise.all(operations).then(() => { 
    // All done 
}); 

或者考慮一下,如果我們的writeFile一諾指明分數版本:

let operations = [] 
(some loop declaration) { 
    // (preparing data etc) 
    operations.push(writeFileWithPromise(fname, fdata)); 
} 
Promise.all(operations).then(() => { 
    // All done 
}); 

,或者如果「循環」是在一個迭代,我們可以變成一個數組,並使用map

Promise.all(
    Array.from(theThingy).map(entry => writeFileWithPromise(entry.fname, entry.fdata)) 
).then(() => { 
    // All done 
}); 
+0

@Benjamin:再次感謝您!但'Array.from'是必須的,因爲我們需要在迭代器上調用'map'(我們沒有直接將它傳遞給'Promise.all')。 –

-1

使用異步:

async.map([['name1', 'data1'], ['name2', 'data2'], ['name3', 'data3']], 
function (item, cb) { 
    fs.writeFile(item[0], item[1], (err, res) => { 
     callback(err, res); 
    }); 
}, 
function(err, results) { 
    // results is now an array of results 
}); 
1

承諾是一種方式。但是,如果您不喜歡它們,則可以使用名爲async的非常知名的庫。

以下是您可以對您的案例進行的操作。下面是它會採用並行處理是什麼樣的一個基本方式:

async.parallel([ 
    function(done) { 
    fs.writeFile(fname1, fdata1, (err) => { 
     done(err, customResults1); 
    }); 
    }, 
    function(done) { 
    fs.writeFile(fname2, fdata2, (err) => { 
     done(err, customResults2); 
    }); 
    } 
], 
// The callback when every function above is done 
function(err, results) { 
    // `results` contains a collection of what you've passed 
    // on the `done` callbacks above 
}); 

更具體到你的使用情況,它看起來像:

async.parallel(
    ['file1', 'file2'].map(function(fname) { 
    return function(done) { 
     fs.writeFile(fname, fdata, (err) => { 
     done(err, customResults); 
     }); 
    }; 
    }), 
    // The callback when every function above is done 
    function(err, results) { 
    // `results` contains a collection of what you've passed 
    // on the `done` callbacks above 
    } 
); 

如果你想在任務運行一個一個爲了您將使用async.series。如果您希望函數將結果傳遞給下一個任務,則可以使用async.waterfall。當然,還有其他功能可以使用,但我提到的功能是一些非常常見的功能。

+2

什麼是反對票?任何原因? – rclai

+0

是的,你建議不需要理由的外部庫。 Promise很好地解決了這個問題,並且內置了它。這是一個寫得很好的答案,我只是不相信它適用於JS中的異步操作的當前狀態。 –

+0

OP沒有說他不想使用外部庫。 – rclai

-1

我已經嘗試了承諾,我不完全相信,有多大的優勢,利用他們在做這樣的事情:

let counter = 0; 
    (some loop declaration) { 
      // (preparing data etc) 
      counter += 1; 
      fs.writeFile(fname, fdata, (err) => { 
        counter -= 1; 
        if (counter==0) done() 
      }); 
    } 

    function done(){ 
     // that's it 
    } 

非常相似,你的初始代碼,但不是使用setTimeout(),當每個操作完成時,我檢查計數器的值。

IMO,這是一樣有效,也許更容易閱讀。

或者,如果你不喜歡有一個單獨的函數來完成了......

let counter = 0; 
    (some loop declaration) { 
      // (preparing data etc) 
      counter += 1; 
      fs.writeFile(fname, fdata, (err) => { 
        counter -= 1; 
        if (counter==0) { 
          // that's it, finish up 
        } 
      }); 
    } 
+1

最後我還是得到一個函數調用,而不是繼續在同一個範圍內的代碼流。 –

+0

@Vadim,當然,但它不一定是一個單獨的功能。我添加了一個替代品。實際上,done()與Promise.all(ops).then()幾乎相同,除非不必管理promise對象。 – Octopus