[英]Node.js Promise within Promise within Promise
我想 ,我想叫Promise
一中Promise
(也许在另一个甚至Promise
)。 过去,我尝试简化我的问题,最终得到更多问题,所以下面有很多内容:
我有以下代码作为名为myModule的模块:
let https = require('https');
module.exports.getApiOne = function(value) {
var params = {..., path = '/api/getOne/' + value, ...};
return getApi(params).then(response => response);
};
module.exports.getApiTwo = function(value) {
var params = {..., path = '/api/getTwo/' + value, ...};
return getApi(params).then(response => response);
};
function getApi(params) {
return new Promise(function(resolve, reject) {
var req = https.request(params, function(res) {
var body = [];
res.on('data', function(chunk) {
body.push(chunk);
});
res.on('end', function() {
try {
body = Buffer.concat(body).toString();
} catch (e) {
reject(e);
}
resolve(body);
});
});
req.on('error', function(err) {
reject(err);
});
req.end();
});
}
在另一个文件上,我有:
const status = require('myModule');
var someObject = {};
function someFunction(inputObject) {
// initialize object
if (!someObject[inputObject.Id]) {
someObject[inputObject.Id] = {};
someObject[inputObject.Id].contact = {};
}
// get object
var objectForThis = someObject[inputObject.Id];
switch (inputObject.someVal) {
case 'test':
//... some code
objectForThis.stage = 'test';
break;
case 'hello':
status.getApiOne('world').then(response => {
console.log(response);
objectForThis.stage = 'zero'
});
break;
default:
someOtherFunction(objectForThis.stage).then(response => {
objectForThis.stage = response;
});
break;
}
someObject[inputObject.Id] = objectForThis;
}
function someOtherFunction(stage) {
var newStage;
new Promise(function(resolve, reject) {
switch (stage) {
case 'zero':
// some code
newStage = 'one';
case 'one':
status.getApiTwo('foo').then(response => {
console.log(response);
newStage = 'two';
/********************************************
I assume, the problem lies here, it's
'resolving' (below) before this sets the new
value for 'newStage'
********************************************/
});
break;
default:
// do nothing
break;
}
});
resolve(newStage);
}
当我打电话
someFunction({id = 1, someValue = 'test'}); // sets 'stage' to 'test'
someFunction({id = 1, someValue = 'hello'}); // sets 'stage' to 'zero'
someFunction({id = 1, someValue = 'foo'}); // sets 'stage' to 'one'
someFunction({id = 1, someValue = 'bar'}); // NOT setting 'stage' to 'two'
这样做的原因是因为Promises是异步的:
logOut("start of file"); new Promise(function(accept){ accept(); }).then(function(){ logOut("inside promise"); }); function makePromise(name) { new Promise(function(accept){ accept(); }).then(function(){ logOut("inside promise inside makePromise " + name); }); }; logOut("value returned from makePromise: " + makePromise("one")); try { // just to prove this makePromise("two").then(function(accept){ accept(); }).then(function(){ logOut("after makePromise"); }); } catch(err) { logOut("Failed to `.then` the return value from makePromise because:\\n" + err.message); } logOut("end of file"); var outputList; function logOut(str){ outputList = outputList || document.getElementById("output"); outputList.insertAdjacentHTML("beforeend", "<li><pre>" + str + "</pre></li>"); }
<ol id="output"></ol>
如上所示,整个程序不会针对.then
语句暂停。 这就是为什么将它们称为Promises的原因:因为其余的代码会在Promise等待解决时继续进行。 此外,如上所述,仅当函数通过then
关键字显式返回值时,才可以从函数返回值。 JavaScript函数不会自动返回最后执行的语句的值。
在下面的演示中,我试图修复您在此问题上打断的文件片段。 然后,我继续将它们包装到我输入的快速单文件系统中
(function(){"use strict"; // NOTE: This setup code makes no attempt to accurately replicate the // NodeJS api. This setup code only tries to concisely mimics // the NodeJS API solely for the purposes of preserving your code // in its present NodeJS form. var modules = {}, require = function(fileName){return modules[fileName]}; for (var i=0; i < arguments.length; i=i+1|0) arguments[i]({exports: modules[arguments[i].name] = {}}, require); })(function https(module, require){"use strict"; ////////////// https.js ////////////// module.exports.request = function(options, withWrapper) { var p, when = {}, wrapper = {on: function(name, handle){ when[name] = handle; }, end: setTimeout.bind(null, function(){ if (p === "/api/getOne/world") when.data("HTTP bar in one 1"); else if (p === "/api/getTwo/foo") when.data("HTTP foo in two 2"); else {console.error("Not stored path: '" + p + "'"); return setTimeout(when.error);} setTimeout(when.end); // setTimeout used for asynchrony }, 10 + Math.random()*30)}; // simulate random http delay setTimeout(withWrapper, 0, wrapper); // almost setImmediate return p = options.path, options = null, wrapper; }; /******* IGNORE ALL CODE ABOVE THIS LINE *******/ }, function myModule(module, require) {"use strict"; ////////////// myModule.js ////////////// const HttpsModule = require('https'); module.exports.getApiOne = function(value) { var params = {path: '/api/getOne/' + value}; // There is no reason for `.then(response => response);`! // It does absolutely nothing. return getApi(params); // .then(response => response); }; module.exports.getApiTwo = function(value) { var params = {path: '/api/getTwo/' + value}; return getApi(params); // .then(response => response); }; function getApi(params) { return new Promise(function(resolve, reject) { var req = HttpsModule.request(params, function(res) { var body = []; res.on('data', function(chunk) { body.push(chunk); }); res.on('end', function() { try { body = body.join("");//Buffer.concat(body).toString(); } catch (e) { reject(e); } resolve(body); }); }); req.on('error', function(err) { reject(err); }); req.end(); }); } }, function main(module, require) {"use strict"; ////////////// top JS script ////////////// const MyStatusModule = require('myModule'); const isPromise = isPrototypeOf.bind(Promise.prototype) var someObject = {}; function someFunction(inputObject) { // initialize object // NOTE: Javascript IS case-sensitive, so `.Id` !== `.id` if (!someObject.hasOwnProperty(inputObject.id)) { someObject[inputObject.id] = {}; someObject[inputObject.id].contact = {}; } // get object var objectForThis = someObject[inputObject.id]; switch (inputObject.someValue) { case 'test': //... some code return objectForThis.stage = 'test'; break; case 'hello': return MyStatusModule.getApiOne('world').then(function (response) { // console.log(response); return objectForThis.stage = 'zero' }); break; default: return someOtherFunction(objectForThis.stage).then(function (response) { return objectForThis.stage = response; }); break; } } function someOtherFunction(stage) { var newStage; // If you return nothing, then you would be calling `.then` on // on `undefined` (`undefined` is the default return value). // This would throw an error. return new Promise(function(resolve, reject) { switch (stage) { case 'zero': // some code newStage = 'one'; resolve(newStage); // you must call `resolve` case 'one': return MyStatusModule.getApiTwo('foo').then(function (response) { // console.log(response); newStage = 'two'; /******************************************** I assume, the problem lies here, it's 'resolving' (below) before this sets the new value for 'newStage' ********************************************/ resolve(newStage); // you must call `resolve` }); break; default: // do nothing resolve(newStage); // you must call `resolve` break; } }); } // tests: function logPromise(){ var a=arguments, input = a[a.length-1|0]; if (isPromise(input)) { for (var c=[null], i=0; i<(a.length-1|0); i=i+1|0) c.push(a[i]); return input.then(logPromise.bind.apply(logPromise, c)); } else console.log.apply(console, arguments); } logPromise("test->test: ", someFunction({id: 1, someValue: 'test'})); // sets 'stage' to 'test' logPromise("hello->zero: ", someFunction({id: 1, someValue: 'hello'})) // sets 'stage' to 'zero' .finally(function(){ // `.finally` is like `.then` without arguments // This `.finally` waits until the HTTP request is done logPromise("foo->one: ", someFunction({id: 1, someValue: 'foo'})) // sets 'stage' to 'one' .finally(function(){ debugger; logPromise("bar->two: ", someFunction({id: 1, someValue: 'bar'})); // NOT setting 'stage' to 'two' }); }); });
如果尚不明显,请不要将上面的代码段复制到您的代码中。 这将破坏您的代码,因为上面的代码片段是使用虚拟Node模块装配的,这些模块旨在产生设定结果。 相反,如果必须复制,则将上述片段中的每个文件(每个文件都包装在一个函数中)复制到代码的相应文件中。 另外,在复制时,请记住不要将伪造的东西复制到明显的“ IGNORE ALL CODE ABOVE THIS LINE
上方的IGNORE ALL CODE ABOVE THIS LINE
指示器上方。 另外,不要忘记进行严格的测试。 我对浏览器JavaScript的了解比对Node JavaScript的了解要多得多,因此有可能(尽管不太可能)引入了潜在的错误源。
someObject[inputObject.id] = objectForThis;
不需要 对于这个问题,我可以给您一个超级简洁的答案。 但是,我觉得“快速”的答案不能使您公正,因为这个特定问题需要更深的理解才能提出解释,而不是简单地阅读别人的解释。 此外,这是Javascript中非常关键的概念。 因此,非常有必要自己提出解释,以免从现在起5分钟后再次遇到相同的麻烦。 因此,我写了下面的教程来指导您找到答案,以便您可以完全理解。
在Javascript中,有表现形式,例如7 - 4
这产生3
。 每个表达式返回一个值,可以供其他表达式使用。 3 * (4 + 1)
首先将4 + 1
为3 * 5
,然后得出15
。 赋值表达式( =
, +=
, -=
, *=
, /=
, %=
, **=
, &=
, |=
和^=
)仅更改左手变量。 任何右侧变量均保持完全相同并包含相同的值:
var p = {}; var ptr = p; // Checkpoint A: console.log('Checkpoint A: p === ptr is now ' + (p === ptr)); ptr = null; // Checkpoint B: console.log('Checkpoint B: p === ptr is now ' + (p === ptr));
让我们检查检查点A的p
和ptr
是什么样子。
从上图可以看出,即使p
和ptr
都指向相同的对象,也要保持分开。 因此,在检查点B,将ptr
更改为其他值不会将p
更改为其他值。 在检查点B,变量p
保持不变,而ptr
现在为null
。
在检查点A,请记住(尽管p
和ptr
是具有不同内存地址的不同变量) p
和ptr
都指向同一对象,因为它们指向的内存位置是相同的索引号。 因此,如果我们在检查点A处更改了该对象,则读/写p
的属性将与读/写ptr
的属性相同,因为您正在更改要指向的数据,而不是哪个变量指向的是什么数据。
function visualize(inputObject) { // displays an object as a human-readable JSON string return JSON.stringify(inputObject); } var p = {}; p.hello = "world"; // Checkpoint 0: console.log('Checkpoint 0: p is ' + visualize(p)); var ptr = p; ptr.foo = "bar"; // Checkpoint A: console.log('Checkpoint A: p is ' + visualize(p) + ' while ptr is ' + visualize(ptr)); ptr = null; // Checkpoint B: console.log('Checkpoint B: p is ' + visualize(p) + ' while ptr is ' + visualize(ptr)); p.foo = p.hello; // Checkpoint C: console.log('Checkpoint C: p is ' + visualize(p) + ' while ptr is ' + visualize(ptr));
如上面的检查点A所示,更改p
与更改ptr
相同。 当我们重新分配对象时该怎么办? 棕褐色的自动垃圾收集器将不再指向要清除的旧对象。
function visualize(inputObject) { // displays an object as a human-readable JSON string return JSON.stringify(inputObject); } var first = {one: "is first"}; first = {uno: "es el primero"}; var second = {two: "is second"}; second = first; console.log("second is " + JSON.stringify(second));
在这方面,函数自变量与变量相同。
var obj = {};
setVariable(obj);
obj.key = "value";
console.log("obj is " + JSON.stringify(obj));
function setVariable(inputVariable){
inputVariable.setValue = "set variable value";
inputVariable = null;
}
是相同的:
var obj = {}; /*function setVariable(*/ var inputVariable = obj; /*){*/ inputVariable.setValue = "set variable value"; inputVariable = null; /*}*/ obj.key = "value"; console.log("obj is " + JSON.stringify(obj));
对象也没有什么不同:
function visualize(inputObject) { // displays an object as a human-readable JSON string return JSON.stringify(inputObject); } var variables = {}; var aliasVars = variables; // Now, `variables` points to the same object as `aliasVars` variables.p = {}; aliasVars.p.hello = "world"; // Checkpoint 0: console.log('Checkpoint 0: variables are ' + visualize(variables)); console.log('Checkpoint 0: aliasVars are ' + visualize(aliasVars)); variables.ptr = aliasVars.p; aliasVars.ptr.foo = "bar"; // Checkpoint A: console.log('Checkpoint A: variables are ' + visualize(variables)); console.log('Checkpoint A: aliasVars are ' + visualize(aliasVars)); variables.ptr = null; // Checkpoint B: console.log('Checkpoint B: variables are ' + visualize(variables)); console.log('Checkpoint B: aliasVars are ' + visualize(aliasVars)); aliasVars.p.foo = variables.p.hello; // Checkpoint C: console.log('Checkpoint C: variables are ' + visualize(variables)); console.log('Checkpoint C: aliasVars are ' + visualize(aliasVars));
接下来是对象符号,只是为了确保我们在同一页面上。
var obj = {};
obj.one = 1;
obj.two = 2;
console.log( "obj is " + JSON.stringify(obj) );
是相同的
var obj = {one: 1, two: 2};
console.log( "obj is " + JSON.stringify(obj) );
是相同的
console.log( "obj is " + JSON.stringify({one: 1, two: 2}) );
是相同的
console.log( "obj is {\\"one\\":1,\\"two\\":2}" );
hasOwnProperty
允许我们确定对象是否具有属性。
var obj = {}; // Checkpoint A: console.log("Checkpoint A: obj.hasOwnProperty(\\"hello\\") is " + obj.hasOwnProperty("hello")); console.log("Checkpoint A: obj[\\"hello\\"] is " + obj["hello"]); obj.hello = "world"; // now set the variable // Checkpoint B: console.log("Checkpoint B: obj.hasOwnProperty(\\"hello\\") is " + obj.hasOwnProperty("hello")); console.log("Checkpoint B: obj[\\"hello\\"] is " + obj["hello"]); obj.hello = undefined; // Checkpoint C: console.log("Checkpoint C: obj.hasOwnProperty(\\"hello\\") is " + obj.hasOwnProperty("hello")); console.log("Checkpoint C: obj[\\"hello\\"] is " + obj["hello"]); delete obj.hello; // Checkpoint D: console.log("Checkpoint D: obj.hasOwnProperty(\\"hello\\") is " + obj.hasOwnProperty("hello")); console.log("Checkpoint D: obj[\\"hello\\"] is " + obj["hello"]);
javascript中的原型对于理解hasOwnProperty
至关重要,其工作原理如下:当在对象中未找到属性时,将检查对象的__proto__以获取该对象。 当对象的__proto__不具有该属性时,将检查该对象的__proto__的__proto__以获取该属性。 只有在到达没有__proto__的对象之后,浏览器才假定所需的属性不存在。 __proto__如下所示。
var obj = {}; console.log('A: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo")); console.log('A: obj.foo is ' + obj.foo); obj.__proto__ = { foo: 'value first' }; console.log('B: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo")); console.log('B: obj.foo is ' + obj.foo); obj.foo = 'value second'; console.log('C: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo")); console.log('C: obj.foo is ' + obj.foo); delete obj.foo; console.log('D: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo")); console.log('D: obj.foo is ' + obj.foo); delete obj.__proto__.foo; console.log('E: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo")); console.log('E: obj.foo is ' + obj.foo);
实际上,我们甚至可以存储对__proto__的引用,并在对象之间共享该引用。
var dog = {noise: "barks"}; var cat = {noise: "meow"}; var mammal = {animal: true}; dog.__proto__ = mammal; cat.__proto__ = mammal; console.log("dog.noise is " + dog.noise); console.log("dog.animal is " + dog.animal); dog.wagsTail = true; cat.clawsSofa = true; mammal.domesticated = true; console.log("dog.wagsTail is " + dog.wagsTail); console.log("dog.clawsSofa is " + dog.clawsSofa); console.log("dog.domesticated is " + dog.domesticated); console.log("cat.wagsTail is " + cat.wagsTail); console.log("cat.clawsSofa is " + cat.clawsSofa); console.log("cat.domesticated is " + cat.domesticated);
但是,上面的语法非常糟糕,因为在创建对象之后更改__proto__很不好。 因此,解决方案是在创建对象的同时设置__proto__。 这称为构造函数。
function Mammal(){} // Notice how Mammal is a function, so you must do Mammal.prototype Mammal.prototype.animal = true; var dog = new Mammal(); // Notice how dog is an instance object of Mammal, so do NOT do dog.prototype dog.noise = "bark"; var cat = new Mammal(); cat.noise = "meow"; console.log("dog.__proto__ is Mammal is " + (dog.__proto__===Mammal)); console.log("cat.__proto__ is Mammal is " + (cat.__proto__===Mammal)); console.log("dog.__proto__ is Mammal.prototype is " + (dog.__proto__===Mammal.prototype)); console.log("cat.__proto__ is Mammal.prototype is " + (cat.__proto__===Mammal.prototype)); console.log("dog.noise is " + dog.noise); console.log("dog.animal is " + dog.animal); dog.wagsTail = true; cat.clawsSofa = true; Mammal.prototype.domesticated = true; console.log("dog.wagsTail is " + dog.wagsTail); console.log("dog.clawsSofa is " + dog.clawsSofa); console.log("dog.domesticated is " + dog.domesticated); console.log("cat.wagsTail is " + cat.wagsTail); console.log("cat.clawsSofa is " + cat.clawsSofa); console.log("cat.domesticated is " + cat.domesticated);
其次,Javascript中的this
对象不像Java中那样引用实例。 而是,以这种方式调用函数时,Java语言中的this
引用表达式object.property()
的object
。 如果未通过这种方式调用函数,则this
对象在严格模式下引用undefined
,而在宽松模式下引用window
。
"use strict"; // "use strict"; at the VERY top of the file ensures strict mode function logThis(title){ console.log(title + "`this === undefined` as " + (this === undefined)); if (this !== undefined) console.log(title + "`this.example_random_name` as " + this.example_random_name); } logThis.example_random_name = "log this raw function"; logThis("logThis() has "); var wrapper = {logThis: logThis, example_random_name: "wrapper around logThis"}; wrapper.logThis("wrapper.logThis has "); var outer = {wrapper: wrapper, example_random_name: "outer wrap arounde"}; outer.wrapper.logThis("outer.wrapper.logThis has ");
我们最终可以利用所有这些知识并将其外推到实际示例中。
var someObject = {}; function someFunction(inputObject) { if (!someObject.hasOwnProperty(inputObject.id)) { someObject[inputObject.id] = {}; someObject[inputObject.id].contact = {}; } // get object var objectForThis = someObject[inputObject.id]; objectForThis.stage = inputObject.stage; } var setTo = {}; setTo.id = 1; setTo.stage = "first stage"; someFunction(setTo); console.log("someObject is " + JSON.stringify(someObject));
首先,让我们内联函数和setTo
var someObject = {}; var setTo = {id: 1, stage: "first stage"}; /*function someFunction(*/ var inputObject = setTo; /*) {*/ if (!someObject.hasOwnProperty(inputObject.id)) { someObject[inputObject.id] = {}; someObject[inputObject.id].contact = {}; } // get object var objectForThis = someObject[inputObject.id]; objectForThis.stage = inputObject.stage; /*}*/ console.log("someObject is " + JSON.stringify(someObject));
接下来,让我们内联setTo
对象。
var someObject = {};
var setTo = {id: 1, stage: "first stage"};
if (!someObject.hasOwnProperty(setTo.id)) {
someObject[setTo.id] = {};
someObject[setTo.id].contact = {};
}
// get object
var objectForThis = someObject[setTo.id];
objectForThis.stage = setTo.stage;
console.log("someObject is " + JSON.stringify(someObject));
然后:
var someObject = {}; if (!someObject.hasOwnProperty(1)) { someObject[1] = {}; someObject[1].contact = {}; } // get object var objectForThis = someObject[1]; objectForThis.stage = "first stage"; console.log("someObject is " + JSON.stringify(someObject));
要直观地演示指针:
var someObject = {}; if (!someObject.hasOwnProperty(1)) { var createdObject = {}; someObject[1] = createdObject; someObject[1].contact = {}; } // get object var objectForThis = someObject[1]; console.log("createdObject === objectForThis is " + (createdObject === objectForThis)); objectForThis.stage = "first stage"; console.log("someObject is " + JSON.stringify(someObject));
如果您还有其他问题,请告诉我。
如果您只是看一眼,那么请不要忘记阅读以上全文。 我向你保证,我在上面的文章要短一些,并且比在互联网上其他任何地方都可以尝试深入地研究Javascript。
不,您的someOtherFunction
不应使用new Promise
。 您应该只链接到status.getApiTwo('foo')
调用,或者使用Promise.resolve
创建立即解析的Promise.resolve
。 这样,它将始终返回承诺,就像您在someFunction
的调用someFunction
期望的那样。
function someOtherFunction(stage) {
switch (stage) {
case 'zero':
// some code
return Promise.resolve('one');
case 'one':
return status.getApiTwo('foo').then(response => {
// ^^^^^^
console.log(response);
return 'two';
// ^^^^^^
});
default:
// do nothing
return Promise.resolve(undefined);
}
}
或者,您可以使用async
/ await
:
async function someOtherFunction(stage) {
switch (stage) {
case 'zero':
// some code
return 'one';
case 'one':
const response = await status.getApiTwo('foo');
console.log(response);
return 'two';
default:
// do nothing
break;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.