簡體   English   中英

第一個承諾解決后返回

[英]Return when first promise resolves

目標

我在數組中有一堆文件名,並且想讀取存在的第一個文件的內容。 它們是配置文件,因此順序是確定性很重要,因此我不能使用.race() 我下面的版本按順序映射每個文件,嘗試加載它,如果加載成功,則調用resolve。

問題

以下是此實現的幾個問題:

  1. 調用resolve(...)實際上並不會退出循環,因此該程序即使在不需要時也會打開列表中的每個文件。
  2. 拒絕條件( this is required to reject when we don't receive any files拒絕的條件),看起來像是黑客。 但是,如果不在這里,那么承諾就永遠不會被拒絕。
  3. 解析代碼看起來像可疑的反模式。

有沒有更好的方法來構造此結構? 我可能只用一個Promise.filter調用就可以做到這Promise.filter ,但是如果不需要的話,我不想查詢每個文件。

謝謝

var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var _ = require('lodash'); 

new Promise((resolve, reject) => {
    // Resolve with the first of the files below that exists
    return Promise.mapSeries(
        ['./file_that_doesntexist.json', '../file_that_might_exist.json', './file_that_doesnt_exist_either.json', '../file_that_exists.json']
        , (filename) => fs.readFileAsync(filename, 'utf-8')
        .then(file => {
            resolve([filename, file]);
            return true;
        })
        .catch(_.stubFalse)
    )
    .then(files => { // this is required to reject when we don't receive any files
        if(!files.some(x => x))
            reject('did not receive any files');
    });
})
.then(function([filename, configFile]) {
    // do something with filename and configFile
})
.catch(function(err) { 
    console.error(err)
})

如果要順序迭代,只需使用遞歸方法:

var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));

function readFirstOf(filenames)
    if (!filenames.length)
        return Promise.reject(new Error('did not receive any files'));

    return fs.readFileAsync(filenames[0], 'utf-8')
    .then(file =>
        [filenames[0], file]
    , err =>
        readFirstOf(filenames.slice(1))
    );
}

readFirstOf(['./file_that_doesntexist.json', '../file_that_might_exist.json', './file_that_doesnt_exist_either.json', '../file_that_exists.json'])
.then(function([filename, configFile]) {
    // do something with filename and configFile
})
.catch(function(err) { 
    console.error(err)
})

如果要嘗試並行讀取所有內容並選擇列表中的第一個成功對象,則可以使用Promise.map + Promise.map .reflect()然后僅過濾結果(例如,通過_.find )。

這可以通過遞歸來實現,也可以通過使用Array#reduce()構建catch鏈來實現:

var paths = ['./file_that_doesntexist.json', '../file_that_might_exist.json', './file_that_doesnt_exist_either.json', '../file_that_exists.json'];

// Resolve with the first of the files below that exists
paths.reduce(function(promise, path) {
    return promise.catch(function(error) {
        return fs.readFileAsync(path, 'utf-8').then(file => [path, file]); 
    });
}, Promise.reject())
.then(function([filename, configFile]) {
    // do something with filename and configFile
})
.catch(function(err) { 
    console.error('did not receive any files', err);
});

捕獲鏈確保每次fs.readFileAsync(path, 'utf-8')失敗時,都嘗試下一個路徑。

第一個成功的fs.readFileAsync(path, 'utf-8')將進入.then(function([filename, configFile]) {...}

完全失敗將落入.catch(function(err) {...}

有這種駭人聽聞的方法可以巧妙地解決這個問題。 您可以像這樣invert承諾。

var invert = pr => pr.then(v => Promise.reject(v), x => Promise.resolve(x));

實際上,當與Promise.all()一起使用時, Promise.all()可以通過忽略被拒絕的對象來獲得第一個解決承諾,因此非常方便。 我的意思是倒立時,拒絕了所有的承諾(解決)可能會被忽視,而第一解析(拒絕)一個被在抓.catch()階段Promise.all() 涼..!

看這個;

 var invert = pr => pr.then(v => Promise.reject(v), x => Promise.resolve(x)), promises = [Promise.reject("No such file"), Promise.reject("No such file either"), Promise.resolve("This is the first existing files content"), Promise.reject("Yet another missing file"), Promise.resolve("Another file content here..!")]; Promise.all(promises.map(pr => invert(pr))) .catch(v => console.log(`First successfully resolving promise is: ${v}`)); 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM