繁体   English   中英

Promise中的Promise中的Node.js Promise

[英]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函数不会自动返回最后执行的语句的值。

在此处查看我的网站以获取有关Promises的更多信息。

在下面的演示中,我试图修复您在此问题上打断的文件片段。 然后,我继续将它们包装到我输入的快速单文件系统中

 (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 + 13 * 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的pptr是什么样子。

从上图可以看出,即使pptr都指向相同的对象,也要保持分开。 因此,在检查点B,将ptr更改为其他值不会将p更改为其他值。 在检查点B,变量p保持不变,而ptr现在为null

在检查点A,请记住(尽管pptr是具有不同内存地址的不同变量) pptr都指向同一对象,因为它们指向的内存位置是相同的索引号。 因此,如果我们在检查点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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM