Promise中的Promise中的Node.js Promise

角色扮演

我正在尝试在一个PromisePromise(甚至在另一个内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函数不会自动返回最后执行的语句的值。

在此处访问我的网站以获取有关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指示器上方另外,不要忘记进行严格的测试。我对浏览器JavaScript的了解比对Node JavaScript的了解要多得多,因此有可能(尽管不太可能)引入了潜在的错误源。



someObject[inputObject.id] = objectForThis; 不需要

对于这个问题,可以给您一个超级简洁的答案。但是,我觉得“快速”的答案不能使您公平,因为这个特定问题需要更深的理解才能提出解释,而不是简单地阅读别人的解释。此外,这是Javascript中非常关键的概念。因此,非常有必要自己提出解释,以免从现在起5分钟后再次遇到相同的麻烦。因此,我写了下面的教程来指导您找到答案,以便您可以完全理解。

在Javascript中,有诸如7 - 4yields之类的表达式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));

让我们来看看什么pptr什么样子的检查点A.

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

在检查点A,请记住(尽管pptr是具有不同内存地址的不同变量),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);

其次,thisJavascript中对象不像Java中那样引用实例。相反,当以这种方式调用函数时thisin 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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章