Programing

중첩 된 약속은 node.js에서 정상입니까?

lottogame 2020. 12. 24. 23:22
반응형

중첩 된 약속은 node.js에서 정상입니까?


node.js를 배우는 동안 내가 2 주 동안 어려움을 겪고있는 문제는 node.js를 사용하여 동기 프로그래밍을 수행하는 방법입니다. 나는 내가 어떻게 순차적으로 일을 시도하더라도 항상 중첩 된 약속으로 끝남을 발견했다 . 유지 보수 가능성까지 Promise Chaining에 도움이되는 Q와 같은 모듈이 있음을 발견했습니다.

조사를하면서 이해하지 못하는 것은 Promise.all(), Promise.resolve()그리고 Promise.reject(). Promise.reject이름으로 거의 설명이 필요하지 않지만 응용 프로그램을 작성할 때 응용 프로그램의 동작을 손상시키지 않고 함수 또는 개체에 이러한 항목을 포함하는 방법에 대해 혼란스러워합니다.

Java 또는 C #과 같은 프로그래밍 언어에서 올 때 node.js에 대한 학습 곡선이 분명합니다. 여전히 남아있는 질문은 node.js에서 프라 미스 체인이 정상인지 (모범 사례)입니다.

예:

driver.get('https://website.com/login').then(function () {
    loginPage.login('company.admin', 'password').then(function () {
        var employeePage = new EmployeePage(driver.getDriver());

        employeePage.clickAddEmployee().then(function() {
            setTimeout(function() {
                var addEmployeeForm = new AddEmployeeForm(driver.getDriver());

                addEmployeeForm.insertUserName(employee.username).then(function() {
                    addEmployeeForm.insertFirstName(employee.firstName).then(function() {
                        addEmployeeForm.insertLastName(employee.lastName).then(function() {
                            addEmployeeForm.clickCreateEmployee().then(function() {
                                employeePage.searchEmployee(employee);
                            });
                        });
                    });
                });
            }, 750);
        });
    });
});

아니요, Promises의 가장 큰 장점 중 하나는 비동기 코드를 중첩되지 않고 선형으로 유지할 수 있다는 것입니다 (연속 전달 스타일의 콜백 지옥).

Promise는 연속 전달 스타일로 잃는 반환 문과 오류 던지기를 제공합니다.

반환 된 값을 연결할 수 있도록 비동기 함수에서 프라 미스를 반환해야합니다.

예를 들면 다음과 같습니다.

driver.get('https://website.com/login')
  .then(function() {
    return loginPage.login('company.admin', 'password')
  })
  .then(function() {
    var employeePage = new EmployeePage(driver.getDriver());
    return employeePage.clickAddEmployee();
  })
  .then(function() {
    setTimeout(function() {
      var addEmployeeForm = new AddEmployeeForm(driver.getDriver());

      addEmployeeForm.insertUserName(employee.username)
        .then(function() {
          return addEmployeeForm.insertFirstName(employee.firstName)
        })
        .then(function() {
          return addEmployeeForm.insertLastName(employee.lastName)
        })
        .then(function() {
          return addEmployeeForm.clickCreateEmployee()
        })
        .then(function() {
          return employeePage.searchEmployee(employee)
        });
    }, 750);
});

Promise.all프라 미스의 배열을 취하고 모든 프라 미스가 해결되면 해결합니다. 하나가 거부되면 어레이가 거부됩니다. 이를 통해 직렬이 아닌 동시에 비동기 코드를 실행하고 모든 동시 함수의 결과를 기다릴 수 있습니다. 스레드 모델에 익숙하다면 스레드 생성 후 결합을 고려하십시오.

예:

addEmployeeForm.insertUserName(employee.username)
    .then(function() {
        // these two functions will be invoked immediately and resolve concurrently
        return Promise.all([
            addEmployeeForm.insertFirstName(employee.firstName),
            addEmployeeForm.insertLastName(employee.lastName)
        ])
    })
    // this will be invoked after both insertFirstName and insertLastName have succeeded
    .then(function() {
        return addEmployeeForm.clickCreateEmployee()
    })
    .then(function() {
        return employeePage.searchEmployee(employee)
    })
    // if an error arises anywhere in the chain this function will be invoked
    .catch(function(err){
        console.log(err)
    });

Promise.resolve()Promise.reject()만들 때 사용되는 메서드 Promise입니다. 콜백 대신 Promise로 작업 할 수 있도록 콜백을 사용하여 비동기 함수를 래핑하는 데 사용됩니다.

Resolve 는 promise를 해결 / 이행합니다 (즉 then, 결과 값 으로 연결된 메서드가 호출 됨을 의미 합니다).
Reject 는 promise를 거부합니다 (즉, 연결된 then메서드가 호출되지 않지만 catch발생한 오류와 함께 첫 번째 연결 메서드가 호출됨을 의미합니다).

나는 setTimeout당신의 프로그램 동작을 보존하기 위해 당신의 자리를 떠났지만 불필요 할 것 같습니다.


정말 추하고 디버그 / 이해하기 어려운 중첩 된 체인 대신 async라이브러리를 사용하고 사용 하십시오 async.series.

async.series([
    methodOne,
    methodTwo
], function (err, results) {
    // Here, results is the value from each function
    console.log(results);
});

Promise.all(iterable)메서드는 반복 가능한 인수의 모든 약속이 해결되면 해결되는 약속을 반환하거나 거부하는 첫 번째 전달 된 약속의 이유와 함께 거부합니다.

var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, "foo");
}); 

Promise.all([p1, p2, p3]).then(function(values) { 
  console.log(values); // [3, 1337, "foo"] 
});

Promise.resolve(value)메서드는 주어진 값으로 해결되는 Promise 개체를 반환합니다. 값이 thenable이면 (즉 then 메소드가있는 경우) 반환 된 promise는 thenable을 "따라"최종 상태를 채택합니다. 그렇지 않으면 반환 된 약속이 값으로 이행됩니다.

var p = Promise.resolve([1,2,3]);
p.then(function(v) {
  console.log(v[0]); // 1
});

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all


불필요한 중첩을 제거했습니다. 'bluebird'(내가 선호하는 Promise 라이브러리) http://bluebirdjs.com/docs/api-reference.html의 구문을 사용합니다.

var employeePage;

driver.get('https://website.com/login').then(function() {
    return loginPage.login('company.admin', 'password');
}).then(function() {
    employeePage = new EmployeePage(driver.getDriver());    
    return employeePage.clickAddEmployee();
}).then(function () {
    var deferred = Promise.pending();
    setTimeout(deferred.resolve,750);
    return deferred.promise;
}).then(function() {
    var addEmployeeForm = new AddEmployeeForm(driver.getDriver());
    return Promise.all([addEmployeeForm.insertUserName(employee.username),
                        addEmployeeForm.insertFirstName(employee.firstName),
                        addEmployeeForm.insertLastName(employee.lastName)]);
}).then(function() {
    return addEmployeeForm.clickCreateEmployee();
}).then(function() {
    return employeePage.searchEmployee(employee);
}).catch(console.log);

모든 질문에 대한 예제를 포함하도록 코드를 수정했습니다.

  1. promise로 작업 할 때 비동기 라이브러리를 사용할 필요가 없습니다. 약속은 그 자체로 매우 강력하며 비동기와 같은 약속과 라이브러리를 혼합하는 안티 패턴이라고 생각합니다.

  2. Normally you should avoid using the var deferred = Promise.pending() style...unless

'when wrapping a callback API that doesn't follow the standard convention. Like setTimeout:'

https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns

For the setTimeout example..create a 'deferred' promise...resolve the promise inside setTimeout and then return the promise outside setTimeout. This might seem a little unintuitive. Look at this example, I answered another question. Q.js promise with node. Missing error handler on `socket`. TypeError: Cannot call method 'then' of undefined

Normally, you can get away with using Promise.promisify(someFunction) to convert a callback type function into a Promise returning function.

  1. Promise.all Lets say your are making multiple calls to an service that return asynchronously. If they don't depend on each other, you can make the calls simultaneously.

Just pass the function calls as an array. Promise.all([promiseReturningCall1, promiseReturningCall2, promiseReturningCall3]);

  1. Finally add a catch block to the very end..to make sure you catch any error. This will catch any exception anywhere in the chain.

I just answered a similar question where I explained a technique that uses generators to flatten Promise chains in a nice way. The technique gets its inspiration from coroutines.

Take this bit of code

Promise.prototype.bind = Promise.prototype.then;

const coro = g => {
  const next = x => {
    let {done, value} = g.next(x);
    return done ? value : value.bind(next);
  }
  return next();
};

Using it, you can transform your deeply-nested Promise chain into this

coro(function* () {
  yield driver.get('https://website.com/login')
  yield loginPage.login('company.admin', 'password');
  var employeePage = new EmployeePage(driver.getDriver());
  yield employeePage.clickAddEmployee();
  setTimeout(() => {
    coro(function* () {
      var addEmployeeForm = new AddEmployeeForm(driver.getDriver());
      yield addEmployeeForm.insertUserName(employee.username);
      yield addEmployeeForm.insertFirstName(employee.firstName);
      yield addEmployeeForm.insertLastName(employee.lastName);
      yield addEmployeeForm.clickCreateEmployee();
      yield employeePage.searchEmployee(employee);
    }());
  }, 750);
}());

Using named generators, we can make that even more legible

// don't forget to assign your free variables
// var driver = ...
// var loginPage = ...
// var employeePage = new EmployeePage(driver.getDriver());
// var addEmployeeForm = new AddEmployeeForm(driver.getDriver());
// var employee = ...

function* createEmployee () {
  yield addEmployeeForm.insertUserName(employee.username);
  yield addEmployeeForm.insertFirstName(employee.firstName);
  yield addEmployeeForm.insertLastName(employee.lastName);
  yield addEmployeeForm.clickCreateEmployee();
  yield employeePage.searchEmployee(employee);
}

function* login () {
  yield driver.get('https://website.com/login')
  yield loginPage.login('company.admin', 'password');
  yield employeePage.clickAddEmployee();
  setTimeout(() => coro(createEmployee()), 750);
}

coro(login());

However, this only scratches the surface of what's possible using coroutines to control the flow of promises. Read the answer I linked above that demonstrates some of the other advantages and capabilities of this technique.

If you do intend to use coroutines for this purpose, I encourage you to check out the co library.

Hope this helps.

PS not sure why you're using setTimeout in this fashion. What is the point of waiting for 750 ms specifically ?


Your next step is to go from nesting to chaining. You need to realize that each promise is an isolated promise that can be chained in a parent promise. In other words, you can flatten the promises to a chain. Each promise result can be passed to the next one.

Here is a great blog post about it: Flattening Promise Chains. It uses Angular but you can ignore that and look at how a deep nesting of promises turns into a chain.

Another good answer is right here on StackOverflow: Understanding javascript promises; stacks and chaining.


You can chain promises like this:

driver.get('https://website.com/login').then(function () {
    return loginPage.login('company.admin', 'password')
)}.then(function () {
    var employeePage = new EmployeePage(driver.getDriver());

    return employeePage.clickAddEmployee().then(function() {
        setTimeout(function() {
            var addEmployeeForm = new AddEmployeeForm(driver.getDriver());
        return addEmployeeForm.insertUserName(employee.username).then(function() {
                retun addEmployeeForm.insertFirstName(employee.firstName)
         }).then(function() {
                return addEmployeeForm.insertLastName(employee.lastName)
         }).then(function() {
             return addEmployeeForm.clickCreateEmployee()
         }).then(function () {
             retrun employeePage.searchEmployee(employee);
        })}, 750);
});

}); });

ReferenceURL : https://stackoverflow.com/questions/35805603/are-nested-promises-normal-in-node-js

반응형