正确使用带有 Await/Async 的 Promise
Posted
技术标签:
【中文标题】正确使用带有 Await/Async 的 Promise【英文标题】:Using Promises with Await/Async Correctly 【发布时间】:2018-05-18 05:44:53 【问题描述】:我在理解 Promise 功能的工作原理时遇到了一些问题,我以前使用过 Bluebird,但我想尝试学习新的 await/async 标准,以便提高程序员的水平。我使用了 async/await 并在我认为合适的地方创建了 Promise,但是这些功能仍然无序执行。
我正在使用 Webpack 在最新版本的 Node 上运行它,我没有收到任何有意义的错误。它运行良好,只是没有像预期的那样。我运行时的输出是:
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.');
,
;
【问题讨论】:
从异步函数返回承诺是没有意义的。然后它根本不需要异步。而且你永远不会打电话给resolve
另外,你应该在错误时拒绝()!
也许使用 fetch api 会更简单,它会返回一个 promise 并且有点像 $.ajax
: developer.mozilla.org/en-US/docs/Web/API/Fetch_API
我将尝试使用 fetch 而不是 https,该模块上似乎没有那么多文档。谢谢各位!
Promises 是 ES2015 (ES6) 的一部分,async/await
是 ES2017 的一部分。
【参考方案1】:
有点晚了,但以下内容应该可以帮助您,我对代码进行了更改。如果您有任何问题,请告诉我。
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 period 或 active connections 来限制响应数量。如果您需要帮助,请告诉我。
【讨论】:
嗨 HMR,这并没有解决响应的排序问题,但是 cmets 对我自己的理解非常有帮助,所以我很感激!我会尝试从 https 迁移到 fetch 看看这是否简化了一些事情。 如果多个请求的顺序很重要,您可以使用搜索将一组搜索对象映射到承诺并使用Promise.all
(更新后的答案示例)以上是关于正确使用带有 Await/Async 的 Promise的主要内容,如果未能解决你的问题,请参考以下文章