正确使用Aise / Async的Promise

时间:2017-12-04 17:29:00

标签: javascript node.js promise ecmascript-2017

我在理解Promise功能如何工作方面遇到了一些问题,我之前使用过Bluebird但是我想尝试学习新的await / async标准,以便作为程序员进行改进。我已经使用了async / await并在我认为合适的情况下创建了promises,但是函数仍然无序执行。

我在最新版本的Node with Webpack上运行它,我没有收到任何有意义的错误。它运行正常,不如预期。运行时的输出是:

Searching the Web for: Test String
Web search Completed!
Promise { <pending> }
Response Handler Completed!

理想情况下,我希望回复:

Searching the Web for: Test String
Response Handler Completed
Web search Completed

然后返回我的响应处理程序的输出。

有人能发现我的错误吗?

const https = require('https');

// Replace the subscriptionKey string value with your valid subscription key.
const subscriptionKey = '<samplekey>';

const host = 'api.cognitive.microsoft.com';
const path = '/bing/v7.0/search';

const response_handler = async (response) => {
    return new Promise((resolve, reject) => {
      let body = '';
      response.on('data', (d) => {
        body += d;
        resolve(body);
      });
      response.on('end', () => {
        console.log('\nRelevant Headers:\n');
        for (const header in response.headers)
                // header keys are lower-cased by Node.js
          {
          if (header.startsWith('bingapis-') || header.startsWith('x-msedge-')) { console.log(`${header}: ${response.headers[header]}`); }
        }
        body = JSON.stringify(JSON.parse(body), null, '  ');
        //console.log('\nJSON Test Response:\n');
        //console.log(body);
      });
      response.on('error', (e) => {
        console.log(`Error: ${e.message}`);
      });
      console.log('Response Handler Completed!');

    });
};

const bing_web_search = async (search) => {
  return new Promise((resolve, reject) => {
  console.log(`Searching the Web for: ${search}`);
  const request_params = {
    method: 'GET',
    hostname: host,
    path: `${path}?q=${encodeURIComponent(search)}&$responseFilter=${encodeURIComponent('Webpages')}&count=${50}`,
    headers: {
      'Ocp-Apim-Subscription-Key': subscriptionKey,
    },
  };

  const req = https.request(request_params, response_handler);

  console.log('Web search Completed!');
  console.log(req.body);
  req.end();
  });
};

module.exports = {
  search: async (search) => {
    if (subscriptionKey.length === 32) {
       const result = await bing_web_search(search);
       console.log('Search Completed');
    } else {
      console.log('Invalid Bing Search API subscription key!');
      console.log('Please paste yours into the source code.');
    }
  },
};

1 个答案:

答案 0 :(得分:0)

有点晚了但是下面的内容应该让你在路上,我对代码进行了更改。如果您有任何疑问,请告诉我。

const https = require('https');

// Replace the subscriptionKey string value with your valid subscription key.
const subscriptionKey = '<samplekey>';

const host = 'api.cognitive.microsoft.com';
const path = '/bing/v7.0/search';

const response_handler = (resolve,reject) => (response) => { // no need for async, you return a promise
  //this one does not return anything, it's the handler for the response and will resolve
  // or reject accordingly
  let body = '';
  response.on('data', (d) => {
    body += d;
    //cannot resolve yet, we're not done
    //  you can resolve on end maybe? I don't know nodejs http
    //  if end event is called when request fails then end would not
    //  be the correct way either, better use fetch api
    //resolve(body);
  });
  response.on('end', () => {
    console.log('\nRelevant Headers:\n');
    for (const header in response.headers)
    // header keys are lower-cased by Node.js
    {
      if (header.startsWith('bingapis-') || header.startsWith('x-msedge-')) { console.log(`${header}: ${response.headers[header]}`); }
    }
    body = JSON.stringify(JSON.parse(body), null, '  ');
    resolve(body);//resolving the promise returned by bing_web_search
    //console.log('\nJSON Test Response:\n');
    //console.log(body);
  });
  response.on('error', (e) => {
    console.log(`Error: ${e.message}`);
    //need to reject with the error
    reject(e);
  });
  console.log('Response Handler Completed!');

};
//no need to specify async, you are not awaiting anything
//  you are creating a promise, when using non promise asynchronous
//  functions that work with callbacks or event emitting objects
//  you need resolve and reject functions so you have to return
//  new Promise(
//    (resolve,reject)=>somecallbackNeedingFunction((err,result)=>
//      err ? reject(err) : resolve(result)
//    )
//  )
const bing_web_search = (search) => {
  return new Promise((resolve, reject) => {
    console.log(`Searching the Web for: ${search}`);
    const request_params = {
      method: 'GET',
      hostname: host,
      path: `${path}?q=${encodeURIComponent(search)}&$responseFilter=${encodeURIComponent('Webpages')}&count=${50}`,
      headers: {
        'Ocp-Apim-Subscription-Key': subscriptionKey,
      },
    };

    const req = https.request(
      request_params, 
      response_handler(resolve,reject)//passing this resolve and reject
    );
    //no, request not completed, we just started
    console.log('Web search Completed!');
    // console.log(req.body); // nothing to log here
    req.end();
  });
};

module.exports = {
  search: async (search) => {
    if (subscriptionKey.length === 32) {
      //did not change anything bing_web_search returns a promise
      //  so you can just await it
      const result = await bing_web_search(search);
      console.log('Search Completed');
      //this will resolve with the results
      return result
    } else {
      console.log('Invalid Bing Search API subscription key!');
      console.log('Please paste yours into the source code.');
      //the caller of this function can handle the rejection
      return Promise.reject('Invalid Bing Search API subscription key!');
    }
  },
};

<强> [更新]

您的评论建议您不要正确调用搜索或处理它正确返回的承诺。在第一个请求最后返回的一组响应中,您无法控制响应所需的时间。这就是你有Promise.all

的原因
const searchObjects = [s1,s2];
const Fail = function(reason){this.reason=reason;};
Promise.all(
  searchObjects.map(
    searchObject => obj.search(searchObject)
    .then(
      x=>[x,searchObject]//if resolve just pass result
      ,err =>new Fail([err,searchObject])//if reject add a Fail object with some detail
    )
  )
)
.then(
  results => {
    console.log(
      "resolved results:",
      results.filter(([r,_])=>(r&&r.constructor)!==Fail)
    );
    console.log(
      "failed results:",
      results.filter(([r,_])=>(r&&r.constructor)===Fail)
    );
  }
)

如果您有大量搜索,那么您可能想要限制具有特定time periodactive connections的响应数量。如果您需要帮助,请告诉我。