不确定如何在反应中处理这个问题 - 多重承诺

时间:2017-11-16 17:27:01

标签: javascript json reactjs callback promise

因此,最简单的解释方法是我尝试在package.json中搜索所有包,然后使用npm注册表查看是否有新的更新。我完成了大部分工作。接受以下for循环:

import request from 'request'

export const findAllUpdates = (packageInfo) => {
  for(prop in packageInfo) {
    request('https://registry.npmjs.org/' + prop, function(err, resp) {
      json = JSON.parse(resp.body);

      if (json['dist-tags'].latest !== packageInfo[prop].version) {
        // Do something, we are not the same version so we should 
        // update some counter.
      }
    });
  }
}

您的packageInfokey=>value的对象,它表示来自package.json和package-lock.json或yarn.lock

的依赖项或dev依赖项。

重要的是我们在上面的函数中看看你安装了什么,然后我们使用注册表来获取包信息并将注册表上的最新版本与你安装的版本进行比较然后我们想要理想情况下,用总计数更新组件的状态。

问题是,我们已经进入了回调地狱,特别是使用for循环,循环遍历每个包,发出请求。

我无法创建变量并将响应存储在那里,因为在请求发出后无法访问它。我无法看到在这里使用事件是如何工作的,因为你可能有40个软件包作为dep或dev安装,并且需要触发很多事件。

最后,正确的解决方案可能是使用承诺,但承诺的整个概念是使用.then(() => {}).catch(() => {})的更多回调,这使我回到原点。

目标是在组件中调用它,并使用必须更新的包(或至少包含新版本)的包总数更新该组件的状态

有什么想法吗?我是不是错了?

2 个答案:

答案 0 :(得分:1)

你正好看待承诺。

首先要给自己一个启用许可的request版本。 util中有一个promisify函数,它接受一个Node-callback风格的函数并返回一个启用promise的版本。所以:

import util from 'util';
// ...
const prequest = util.promisify(request);

(还有一个npm模块,promisify,可以一次完成整个API。)

然后,执行您的请求,在数组中收集promise(并使用then进行任何后处理; then返回一个新的promise,所以我们仍然很好),并使用Promise.all等待他们全部解决(或其中任何一个拒绝);

所有在一起:

import request from 'request';
import util from 'util';

const prequest = util.promisify(request);

export const findAllUpdates = (packageInfo) => {
  const updates = []
  for (const prop in packageInfo) {
    updates.push(prequest('https://registry.npmjs.org/' + prop).then(resp => {
      const json = JSON.parse(resp.body);

      if (json['dist-tags'].latest !== packageInfo[prop].version) {
        // Do something, we are not the same version so we should 
        // update some counter.
      }

      // Perhaps return something here, it will be the resolution
      // for the promise for this request; otherwise, the promise
      // will resolve with `undefined`.
    }));
  }
  return Promise.all(updates);
};

整个函数的承诺将解析为数组中每个promise的结果数组(按顺序),或者(再次)拒绝它们中的任何一个拒绝。

我希望能够建议async / await,但目前并没有为一堆并行承诺带来太大的影响。 (偶尔会有await.all关于做Promise.all做什么的讨论,但它还没有流行[还没有?]。)

尽管如此,如果我们稍微分解一下,async / await会更好一些:

import request from 'request';
import util from 'util';

const prequest = util.promisify(request);

const checkOne = async (prop) => {
  const resp = await prequest('https://registry.npmjs.org/' + prop);
  const json = JSON.parse(resp.body);
  if (json['dist-tags'].latest !== packageInfo[prop].version) {
    // Do something, we are not the same version so we should 
    // update some counter.
  }

  // Perhaps return something here, it will be the resolution
  // for the promise for this request; otherwise, the promise
  // will resolve with `undefined`.
};

export const findAllUpdates = (packageInfo) => {
  const updates = []
  for (const prop in packageInfo) {
    updates.push(checkOne(prop);
  }
  return Promise.all(updates);
};

当然,如果packageInfo中的所有属性都是“自己的”属性(未继承),findAllUpdates会变得更简单:

export const findAllUpdates = (packageInfo) => {
  return Promise.all(Object.keys(packageInfo).map(checkOne));
};

附注:我在上面添加了一些遗漏声明。

答案 1 :(得分:0)

我建议使用Promise。您可以进行多次异步调用,然后使用Promise.all等待它们。

如果您还从" findAllUpdates"返回承诺。方法,然后调用者很容易在信息可用时更新自己的状态。

在React中就像是:

findAllUpdates(..)
  .then(result => this.setState({
    packagesNeedUpdate: result.filter(p => !p.latest).length
  }));

我在下面创建了一个使用promises的简单示例。它会伪造请求,但在其他方面应该非常准确。



// Fake requests. Will return version '1.0.0' after one second.
const makeRequest = url => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(JSON.stringify({
        'dist-tags': {
          'latest': '1.0.0'
        }
      }, null, 2));
    }, 1000);
  });
}

const findAllUpdates = (packageInfo) => {
  return new Promise(resolve => {
    const promises = Object.keys(packageInfo)
      .map(prop => makeRequest('https://registry.npmjs.org/' + prop).then(JSON.parse));
      
    Promise.all(promises) // Wait for all promises
      .then(jsons => {
        const results = Object.keys(packageInfo)
          .map((prop, i) => {
            const latestInNpm = jsons[i]['dist-tags'].latest;
            const current = packageInfo[prop].version
            return {
              prop,
              latest: latestInNpm === current
            }
          });
        resolve(results); // Return result to caller
      });
  });
}

function run() {
  console.log('Loading..');
  
  const packages = {
    'react': {version: '0.0.1'},
    'angular': {version: '1.0.0'},
    'ember': {version: '0.5.0'},
    'mithril': {version: '0.9.0'}
  };
  
  findAllUpdates(packages).then(result => {
    const needUpdates = result.filter(p => !p.latest).length;
    console.log('Need updates: ', needUpdates);
  });
}

run();