我想我正在尝试在一个Promise
内Promise
(甚至在另一个内Promise
)调用a 。过去,我尝试简化我的问题,最终得到更多问题,所以下面有很多内容:
我有以下代码作为名为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
指示器上方。另外,不要忘记进行严格的测试。我对浏览器JavaScript的了解比对Node JavaScript的了解要多得多,因此有可能(尽管不太可能)引入了潜在的错误源。
someObject[inputObject.id] = objectForThis;
不需要对于这个问题,我可以给您一个超级简洁的答案。但是,我觉得“快速”的答案不能使您公平,因为这个特定问题需要更深的理解才能提出解释,而不是简单地阅读别人的解释。此外,这是Javascript中非常关键的概念。因此,非常有必要自己提出解释,以免从现在起5分钟后再次遇到相同的麻烦。因此,我写了下面的教程来指导您找到答案,以便您可以完全理解。
在Javascript中,有诸如7 - 4
yields之类的表达式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));
让我们来看看什么p
和ptr
什么样子的检查点A.
从上图可以看出,即使p
和ptr
都指向相同的对象,它们也保持分开。因此,在检查点B,更改ptr
为其他值不会更改p
为其他值。在Checkpoint B,变量nowp
保持不变。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
。当我们重新分配对象时该怎么办?不再指向的旧对象将由tan自动垃圾收集器清除。
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);
其次,this
Javascript中的对象不像Java中那样引用实例。相反,当以这种方式调用函数时,this
in Javascript引用object
表达式中的in object.property()
。如果未通过这种方式调用函数,则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。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句