기존 콜백 API를 약속으로 변환하려면 어떻게합니까?


721

약속으로 작업하고 싶지만 다음과 같은 형식의 콜백 API가 있습니다.

1. DOM로드 또는 다른 일회성 이벤트 :

window.onload; // set to callback
...
window.onload = function() {

};

2. 일반 콜백 :

function request(onChangeHandler) {
    ...
}
request(function() {
    // change happened
    ...
});

3. 노드 스타일 콜백 ( "nodeback") :

function getStuff(dat, callback) {
    ...
}
getStuff("dataParam", function(err, data) {
    ...
})

4. 노드 스타일 콜백이있는 전체 라이브러리 :

API;
API.one(function(err, data) {
    API.two(function(err, data2) {
        API.three(function(err, data3) {
            ...
        });
    });
});

약속에서 API로 어떻게 작업합니까? 어떻게 "약속"합니까?


나는 내 자신의 답변을 게시했지만 특정 라이브러리 또는 더 많은 환경 에서이 작업을 수행하는 방법에 대한 답변은 확장되어 있으며 편집도 환영합니다.
Benjamin Gruenbaum

@ Bergi 흥미로운 아이디어입니다. 두 가지 일반적인 접근법 (Promise constructor 및 Deferred Object)을 사용하는 일반적인 대답을하려고했습니다. 나는 두 가지 대안을 대답하려고했습니다. RTFMing 이이 문제를 해결하지만 여기와 버그 추적기 모두 에서이 문제가 자주 발생하므로 '정상적인 질문'이 있다고 생각합니다 .RTFMing은 JS 태그에서 문제의 약 50 %를 해결한다고 생각합니다. 당신은 답변에 기여하거나 매우 감사하게 편집 할 흥미로운 통찰력이 있습니다.
Benjamin Gruenbaum

A가 생성합니까 new Promise상당한 오버 헤드를 추가? Node 앱에서 모든 동기 코드를 제거하기 위해 Promise에서 모든 동기 Noje.js 함수를 래핑하고 싶지만 이것이 모범 사례입니까? 즉, 정적 인수 (예 : 문자열)를 허용하고 계산 된 결과를 반환하는 함수는 약속으로 래핑해야합니까? ... Nodejs에 동기 코드가 없어야한다고 읽었습니다.
Ronnie Royston

1
@RonRoyston 아니오, 약속을 동기식 통화로 감싸는 것은 좋지 않습니다. I / O를 수행 할 수있는 비동기식 통화 만
Benjamin Gruenbaum

답변:


744

약속에는 상태가 있으며 보류 중으로 시작하여 다음과 같이 해결할 수 있습니다.

  • 계산이 성공적으로 완료 되었음을 의미합니다.
  • 거부 계산이 실패했음을 의미한다.

약속 반환 함수 는 절대 던져서 는 안되며 거부를 반환해야합니다. 약속 반환 함수에서 던지면 a } catch { a를 모두 사용해야합니다 .catch. 약속 된 API를 사용하는 사람들은 약속을 던질 것으로 기대하지 않습니다. JS에서 비동기 API가 어떻게 작동하는지 잘 모르겠다면 먼저이 답변을 참조하십시오 .

1. DOM로드 또는 다른 일회성 이벤트 :

따라서 약속을 만드는 것은 일반적으로 약속 시간을 지정하는 것을 의미합니다. 즉, 데이터가 사용 가능하고로 액세스 할 수 있음을 나타 내기 위해 이행 또는 거부 단계로 이동할 때를 의미합니다 .then.

Promise기본 ES6 약속과 같은 생성자 를 지원하는 최신 약속 구현을 통해 :

function load() {
    return new Promise(function(resolve, reject) {
        window.onload = resolve;
    });
}

그런 다음 결과 약속을 다음과 같이 사용하십시오.

load().then(function() {
    // Do things after onload
});

지연을 지원하는 라이브러리를 사용하는 경우 (이 예제에서는 $ q를 사용하지만 나중에 jQuery도 사용합니다) :

function load() {
    var d = $q.defer();
    window.onload = function() { d.resolve(); };
    return d.promise;
}

또는 API와 같은 jQuery를 사용하여 한 번 발생하는 이벤트에 연결하십시오.

function done() {
    var d = $.Deferred();
    $("#myObject").once("click",function() {
        d.resolve();
    });
    return d.promise();
}

2. 일반 콜백 :

이 API는 꽤 일반적이기 때문에… 콜백은 JS에서 일반적입니다. 가지고있는 일반적인 경우에서 살펴 보자 onSuccessonFail:

function getUserData(userId, onLoad, onFail) { 

Promise기본 ES6 약속과 같은 생성자 를 지원하는 최신 약속 구현을 통해 :

function getUserDataAsync(userId) {
    return new Promise(function(resolve, reject) {
        getUserData(userId, resolve, reject);
    });
}

지연을 지원하는 라이브러리를 사용하는 경우 (이 예제에서는 jQuery를 사용하지만 위의 $ q도 사용했습니다) :

function getUserDataAsync(userId) {
    var d = $.Deferred();
    getUserData(userId, function(res){ d.resolve(res); }, function(err){ d.reject(err); });
    return d.promise();
}

jQuery는 또한 다음 과 같이 양식을 $.Deferred(fn)매우 에뮬레이트하는 표현식을 작성할 수있는 장점이있는 new Promise(fn)양식을 제공합니다.

function getUserDataAsync(userId) {
    return $.Deferred(function(dfrd) {
        getUserData(userId, dfrd.resolve, dfrd.reject);
    }).promise();
}

참고 : 여기서는 jQuery 지연 resolvereject메소드가 "분리 가능" 하다는 사실을 이용합니다 . 즉. 그것들은 jQuery.Deferred () 의 인스턴스 에 바인딩됩니다 . 모든 라이브러리가이 기능을 제공하는 것은 아닙니다.

3. 노드 스타일 콜백 ( "nodeback") :

노드 스타일 콜백 (노드 백)은 콜백이 항상 마지막 인수이고 첫 번째 매개 변수가 오류 인 특정 형식을 갖습니다. 먼저 하나를 수동으로 약속합시다.

getStuff("dataParam", function(err, data) { 

에:

function getStuffAsync(param) {
    return new Promise(function(resolve, reject) {
        getStuff(param, function(err, data) {
            if (err !== null) reject(err);
            else resolve(data);
        });
    });
}

지연을 사용하면 다음을 수행 할 수 있습니다 (이 예제에서는 Q를 사용하지만 Q 는 선호 하는 새로운 구문 지원하지만 ).

function getStuffAsync(param) {
    var d = Q.defer();
    getStuff(param, function(err, data) {
        if (err !== null) d.reject(err);
        else d.resolve(data);
    });
    return d.promise;   
}

일반적으로 노드를 염두에두고 설계된 대부분의 약속 라이브러리와 노드 8+의 기본 약속에는 노드 백을 약속하는 방법이 내장되어 있습니다. 예를 들어

var getStuffAsync = Promise.promisify(getStuff); // Bluebird
var getStuffAsync = Q.denodeify(getStuff); // Q
var getStuffAsync = util.promisify(getStuff); // Native promises, node only

4. 노드 스타일 콜백이있는 전체 라이브러리 :

여기에는 황금률이 ​​없으며 하나씩 약속합니다. 그러나 일부 약속 구현을 사용하면 예를 들어 Bluebird에서 노드 백 API를 약속 API로 변환하는 것이 다음과 같이 간단합니다.

Promise.promisifyAll(API);

또는 Node의 기본 약속 으로 :

const { promisify } = require('util');
const promiseAPI = Object.entries(API).map(([key, v]) => ({key, fn: promisify(v)}))
                         .reduce((o, p) => Object.assign(o, {[p.key]: p.fn}), {});

노트:

  • 물론, 당신이 .then핸들러 에있을 때 당신은 일을 약속 할 필요가 없습니다. .then처리기 에서 약속을 반환하면 해당 약속의 값으로 해결되거나 거부됩니다. .then처리기 에서 던지는 것도 좋은 습관이며 약속을 거부합니다. 이것은 유명한 약속 던지기 안전입니다.
  • 실제로 onload는을 사용 addEventListener하지 않아야합니다 onX.

벤자민, 귀하의 사례 2를 편집하고 추가 jQuery 예제를 추가하라는 귀하의 초대를 수락했습니다. 표시되기 전에 동료 검토가 필요합니다. 네가 좋아하길 바래.
Roamer-1888

@ Roamer-1888 제 시간에 그것을보고 받아들이지 않아 거부되었습니다. 가치가있는 것에 대해서는 추가가 유용하지만 유용하다고 생각하지 않습니다.
Benjamin Gruenbaum

2
벤자민 것인지의 여부를 불문 resolve()하고 reject()재사용 가능하도록 작성된, 나는 그것이 형태의의 jQuery 예를 제공하기 때문에 내 제안 편집 관련성을 감행 $.Deferred(fn)그렇지 않으면 부족하다. 하나의 jQuery 예제 만 포함 된 경우 var d = $.Deferred();사람들이 자주 무시되는 $.Deferred(fn)양식 을 사용하도록 권장해야 하기 때문에이 형식이 아닌 등 이 형식이어야한다고 제안합니다 . 이와 같은 대답에서는 jQuery를 사용 libs와 밝히려 생성자 패턴 .
Roamer-1888

Heh, 100 % 공정한 나는 jQuery가 당신을 알고 있다는 것을 몰랐다 $.Deferred(fn). 만약 당신이 다음 15 분 안에 기존 예제 대신 그것을 편집한다면 나는 그것을 제 시간에 승인하려고 노력할 것이다. :)
Benjamin Gruenbaum

7
이것은 좋은 대답입니다. util.promisifyNode.js가 RC 8.0.0부터 코어에 추가 될 것임을 언급하여 업데이트하고 싶을 수도 있습니다 . 작동 방식은 Bluebird Promise.promisify와 크게 다르지 않지만 기본 약속을 원할 경우 추가 종속성이 필요 없다는 이점이 있습니다. 주제에 대해 더 많은 것을 읽고 싶은 사람을 위해 util.promisify에 대한 블로그 게시물을 작성했습니다 .
Bruno

55

오늘, 내가 사용할 수 Promise있는 Node.js일반 자바 스크립트 방법으로.

Promise( KISS 방식으로) 간단한 기본 예 :

일반 자바 스크립트 비동기 API 코드 :

function divisionAPI (number, divider, successCallback, errorCallback) {

    if (divider == 0) {
        return errorCallback( new Error("Division by zero") )
    }

    successCallback( number / divider )

}

Promise 자바 스크립트 비동기 API 코드 :

function divisionAPI (number, divider) {

    return new Promise(function (fulfilled, rejected) {

        if (divider == 0) {
            return rejected( new Error("Division by zero") )
        }

        fulfilled( number / divider )

     })

}

( 이 아름다운 출처를 방문 하는 것이 좋습니다 )

또한 Promise함께 사용할 수 있습니다 async\awaitES7A의 프로그램 흐름 대기하기 위해 fullfiled다음과 같은 결과를 :

function getName () {

    return new Promise(function (fulfilled, rejected) {

        var name = "John Doe";

        // wait 3000 milliseconds before calling fulfilled() method
        setTimeout ( 
            function() {
                fulfilled( name )
            }, 
            3000
        )

    })

}


async function foo () {

    var name = await getName(); // awaits for a fulfilled result!

    console.log(name); // the console writes "John Doe" after 3000 milliseconds

}


foo() // calling the foo() method to run the code

.then()메소드 를 사용하여 동일한 코드를 사용하는 다른 사용법

function getName () {

    return new Promise(function (fulfilled, rejected) {

        var name = "John Doe";

        // wait 3000 milliseconds before calling fulfilled() method
        setTimeout ( 
            function() {
                fulfilled( name )
            }, 
            3000
        )

    })

}


// the console writes "John Doe" after 3000 milliseconds
getName().then(function(name){ console.log(name) })

PromiseNode.js를 기반으로하는 모든 플랫폼에서도 사용할 수 있습니다 react-native.

보너스 : 하이브리드 메소드
(콜백 메소드는 오류 및 결과로 두 개의 매개 변수를 갖는 것으로 가정)

function divisionAPI (number, divider, callback) {

    return new Promise(function (fulfilled, rejected) {

        if (divider == 0) {
            let error = new Error("Division by zero")
            callback && callback( error )
            return rejected( error )
        }

        let result = number / divider
        callback && callback( null, result )
        fulfilled( result )

     })

}

위의 방법은 구식 콜백 및 Promise 사용에 대한 결과에 응답 할 수 있습니다.

도움이 되었기를 바랍니다.


3
이것들은 약속으로 바꾸는 방법을 보여주지 않는 것 같습니다.
Dmitri Zaitsev

33

Node.JS에서 약속으로 함수를 변환하기 전에

var request = require('request'); //http wrapped module

function requestWrapper(url, callback) {
    request.get(url, function (err, response) {
      if (err) {
        callback(err);
      }else{
        callback(null, response);             
      }      
    })
}


requestWrapper(url, function (err, response) {
    console.log(err, response)
})

변환 후

var request = require('request');

function requestWrapper(url) {
  return new Promise(function (resolve, reject) { //returning promise
    request.get(url, function (err, response) {
      if (err) {
        reject(err); //promise reject
      }else{
        resolve(response); //promise resolve
      }
    })
  })
}


requestWrapper('http://localhost:8080/promise_request/1').then(function(response){
    console.log(response) //resolve callback(success)
}).catch(function(error){
    console.log(error) //reject callback(failure)
})

여러 요청을 처리해야 할 경우

var allRequests = [];
allRequests.push(requestWrapper('http://localhost:8080/promise_request/1')) 
allRequests.push(requestWrapper('http://localhost:8080/promise_request/2'))
allRequests.push(requestWrapper('http://localhost:8080/promise_request/5'))    

Promise.all(allRequests).then(function (results) {
  console.log(results);//result will be array which contains each promise response
}).catch(function (err) {
  console.log(err)
});

23

window.onload@ Benjamin 의 제안은로드 후 호출되는지 여부를 감지하지 못하기 때문에 항상 작동 한다고 생각 하지 않습니다. 나는 여러 번 물렸다. 다음은 항상 작동해야하는 버전입니다.

function promiseDOMready() {
    return new Promise(function(resolve) {
        if (document.readyState === "complete") return resolve();
        document.addEventListener("DOMContentLoaded", resolve);
    });
}
promiseDOMready().then(initOnLoad);

1
비동기식으로 호출되도록 "이미 완료된"브랜치를 사용해서는 안 됩니까 setTimeout(resolve, 0)(또는 setImmediate가능한 경우)?
Alnitak

5
@Alnitak resolve동 기적으로 호출하는 것이 좋습니다. Promise의 then핸들러는 동 기적으로 호출 되는지 여부에 관계없이 프레임 워크가 비동기 적으로 호출되도록 보장resolve 합니다.
Jeff Bowman

15

Node.js 8.0.0에는 util.promisify()표준 Node.js 콜백 스타일 API를 Promise를 반환하는 함수에 래핑 할 수 있는 새로운 API가 포함 되어 있습니다. 사용 예 util.promisify()는 다음과 같습니다.

const fs = require('fs');
const util = require('util');

const readFile = util.promisify(fs.readFile);

readFile('/some/file')
  .then((data) => { /** ... **/ })
  .catch((err) => { /** ... **/ });

약속에 대한 향상된 지원 참조


2
이것을 설명하는 두 가지 답변이 이미 있는데 왜 세 번째 답변을 게시해야합니까?
Benjamin Gruenbaum

1
해당 버전의 노드가 릴리스 되었기 때문에 "공식"기능 설명 및 링크가보고되었습니다.
지안 마르코 게 라르 디

14

Node.js 8.0.0의 릴리스 후보에는 모든 기능을 약속하는 기능을 캡슐화 하는 새로운 유틸리티 util.promisify( util.promisify 에 대해 작성 했습니다 )가 있습니다.

그것은 다른 답변에서 제안 된 접근 방식과 크게 다르지 않지만 핵심 방법이며 추가 종속성이 필요하지 않다는 이점이 있습니다.

const fs = require('fs');
const util = require('util');

const readFile = util.promisify(fs.readFile);

그런 다음 readFilenative을 반환하는 메서드가 Promise있습니다.

readFile('./notes.txt')
  .then(txt => console.log(txt))
  .catch(...);

1
이봐, 나 (OP)는 실제로 util.promisify두 번 제안 했습니다 (2014 년이 질문이 쓰여졌을 때와 몇 개월 전-Node의 핵심 멤버로 밀렸으며 Node에있는 현재 버전입니다). 아직 공개되지 않았기 때문에 아직이 답변에 추가하지 않았습니다. 우리는 사용 피드백에 깊이 감사 드리며 릴리즈에 대한 더 나은 문서를 얻기 위해 어떤 함정이 무엇인지 알게되었습니다 :)
Benjamin Gruenbaum

1
또한 util.promisify블로그 게시물에서 약속의 사용자 지정 플래그에 대해 토론하고 싶을 수도 있습니다. :
Benjamin Gruenbaum

@BenjaminGruenbaum util.promisify.custom기호 를 사용하면 util.promisify의 결과를 무시할 수 있다는 사실을 의미 합니까? 솔직히 이것은 의도적 인 미스 였으므로 아직 유용한 사용 사례를 찾지 못했습니다. 아마도 당신은 나에게 입력을 줄 수 있습니까?
Bruno

1
물론, 같은 API를 고려 fs.exists파랑새가 - 노드 규칙을 따르지 않거나 API를 Promise.promisify 잘못을 얻을 것입니다,하지만 util.promisify권리를 가져옵니다.
Benjamin Gruenbaum

7

Node JS에서 JavaScript 기본 약속을 사용할 수 있습니다.

My Cloud 9 코드 링크 : https://ide.c9.io/adx2803/native-promises-in-node

/**
* Created by dixit-lab on 20/6/16.
*/

var express = require('express');
var request = require('request');   //Simplified HTTP request client.


var app = express();

function promisify(url) {
    return new Promise(function (resolve, reject) {
        request.get(url, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                resolve(body);
            }
            else {
                reject(error);
            }
        })
    });
}

//get all the albums of a user who have posted post 100
app.get('/listAlbums', function (req, res) {
    //get the post with post id 100
    promisify('http://jsonplaceholder.typicode.com/posts/100').then(function (result) {
        var obj = JSON.parse(result);
        return promisify('http://jsonplaceholder.typicode.com/users/' + obj.userId + '/albums')
    })
    .catch(function (e) {
        console.log(e);
    })
    .then(function (result) {
        res.end(result);
    })
})

var server = app.listen(8081, function () {
    var host = server.address().address
    var port = server.address().port

    console.log("Example app listening at http://%s:%s", host, port)
})

//run webservice on browser : http://localhost:8081/listAlbums

7

평범한 오래된 바닐라 자바 ​​스크립트를 사용하면 API 콜백을 약속하는 솔루션이 있습니다.

function get(url, callback) {
        var xhr = new XMLHttpRequest();
        xhr.open('get', url);
        xhr.addEventListener('readystatechange', function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    console.log('successful ... should call callback ... ');
                    callback(null, JSON.parse(xhr.responseText));
                } else {
                    console.log('error ... callback with error data ... ');
                    callback(xhr, null);
                }
            }
        });
        xhr.send();
    }

/**
     * @function promisify: convert api based callbacks to promises
     * @description takes in a factory function and promisifies it
     * @params {function} input function to promisify
     * @params {array} an array of inputs to the function to be promisified
     * @return {function} promisified function
     * */
    function promisify(fn) {
        return function () {
            var args = Array.prototype.slice.call(arguments);
            return new Promise(function(resolve, reject) {
                fn.apply(null, args.concat(function (err, result) {
                    if (err) reject(err);
                    else resolve(result);
                }));
            });
        }
    }

var get_promisified = promisify(get);
var promise = get_promisified('some_url');
promise.then(function (data) {
        // corresponds to the resolve function
        console.log('successful operation: ', data);
}, function (error) {
        console.log(error);
});

6

kriskowal의 Q 라이브러리에는 콜백 후 약속 기능이 포함되어 있습니다. 이와 같은 방법 :

obj.prototype.dosomething(params, cb) {
  ...blah blah...
  cb(error, results);
}

Q.ninvoke로 변환 가능

Q.ninvoke(obj,"dosomething",params).
then(function(results) {
});

1
정식 답변은 이미 언급했습니다 Q.denodeify. 도서관 도우미를 강조해야합니까?
Bergi

3
Q Leads를 약속하는 것에 대한 구글로서 이것이 유용하다는 것을 알았습니다.
Ed Sykes

4

콜백을받는 함수가 몇 개 있는데 약속을 반환하려는 경우이 함수를 사용하여 변환을 수행 할 수 있습니다.

function callbackToPromise(func){

    return function(){

        // change this to use what ever promise lib you are using
        // In this case i'm using angular $q that I exposed on a util module

        var defered = util.$q.defer();

        var cb = (val) => {
            defered.resolve(val);
        }

        var args = Array.prototype.slice.call(arguments);
        args.push(cb);    
        func.apply(this, args);

        return defered.promise;
    }
}

4

약속과 비동기가 내장 된 노드 v7.6 이상에서 :

// promisify.js
let promisify = fn => (...args) =>
    new Promise((resolve, reject) =>
        fn(...args, (err, result) => {
            if (err) return reject(err);
            return resolve(result);
        })
    );

module.exports = promisify;

사용하는 방법:

let readdir = require('fs').readdir;
let promisify = require('./promisify');
let readdirP = promisify(readdir);

async function myAsyncFn(path) {
    let entries = await readdirP(path);
    return entries;
}

3

Node.js 8에서는 다음 npm 모듈을 사용하여 객체 메소드 를 즉시 약속 할 수 있습니다 .

https://www.npmjs.com/package/doasync

객체가 변경되지 않도록 util.promisify프록시를 사용합니다 . 메모WeakMaps를 사용하여 수행됩니다). 여기 몇 가지 예가 있어요.

객체로 :

const fs = require('fs');
const doAsync = require('doasync');

doAsync(fs).readFile('package.json', 'utf8')
  .then(result => {
    console.dir(JSON.parse(result), {colors: true});
  });

기능으로 :

doAsync(request)('http://www.google.com')
  .then(({body}) => {
    console.log(body);
    // ...
  });

네이티브를 사용 call하고 apply일부 컨텍스트를 바인딩 할 수도 있습니다 .

doAsync(myFunc).apply(context, params)
  .then(result => { /*...*/ });

2

setTimeout을 처리하기 위해 ES6에서 기본 Promise 를 사용할 수 있습니다 .

enqueue(data) {

    const queue = this;
    // returns the Promise
    return new Promise(function (resolve, reject) {
        setTimeout(()=> {
                queue.source.push(data);
                resolve(queue); //call native resolve when finish
            }
            , 10); // resolve() will be called in 10 ms
    });

}

이 예에서 약속은 실패 할 이유 reject()가 없으므로 호출되지 않습니다.


2

콜백 스타일 항상이 같은 기능 (Node.js를 거의 모든 기능이 스타일) :

//fs.readdir(path[, options], callback)
fs.readdir('mypath',(err,files)=>console.log(files))

이 스타일은 동일한 기능을 가지고 있습니다 :

  1. 콜백 함수는 마지막 인수에 의해 전달됩니다.

  2. 콜백 함수는 항상 첫 번째 인수이므로 오류 객체를 받아들입니다.

따라서 다음과 같이이 스타일로 함수를 변환하는 함수를 작성할 수 있습니다.

const R =require('ramda')

/**
 * A convenient function for handle error in callback function.
 * Accept two function res(resolve) and rej(reject) ,
 * return a wrap function that accept a list arguments,
 * the first argument as error, if error is null,
 * the res function will call,else the rej function.
 * @param {function} res the function which will call when no error throw
 * @param {function} rej the function which will call when  error occur
 * @return {function} return a function that accept a list arguments,
 * the first argument as error, if error is null, the res function
 * will call,else the rej function
 **/
const checkErr = (res, rej) => (err, ...data) => R.ifElse(
    R.propEq('err', null),
    R.compose(
        res,
        R.prop('data')
    ),
    R.compose(
        rej,
        R.prop('err')
    )
)({err, data})

/**
 * wrap the callback style function to Promise style function,
 * the callback style function must restrict by convention:
 * 1. the function must put the callback function where the last of arguments,
 * such as (arg1,arg2,arg3,arg...,callback)
 * 2. the callback function must call as callback(err,arg1,arg2,arg...)
 * @param {function} fun the callback style function to transform
 * @return {function} return the new function that will return a Promise,
 * while the origin function throw a error, the Promise will be Promise.reject(error),
 * while the origin function work fine, the Promise will be Promise.resolve(args: array),
 * the args is which callback function accept
 * */
 const toPromise = (fun) => (...args) => new Promise(
    (res, rej) => R.apply(
        fun,
        R.append(
            checkErr(res, rej),
            args
        )
    )
)

더 간결하게 위의 예에서는 ramda.js를 사용했습니다. Ramda.js는 함수형 프로그래밍을위한 훌륭한 라이브러리입니다. 위의 코드에서 우리는 적용 (예 : javascript function.prototype.apply) 및 append (like javascript function.prototype.push)를 사용했습니다. 따라서 콜백 스타일 함수를 약속 스타일 함수로 변환 할 수 있습니다.

const {readdir} = require('fs')
const readdirP = toPromise(readdir)
readdir(Path)
    .then(
        (files) => console.log(files),
        (err) => console.log(err)
    )

toPromisecheckErr 기능은 맹렬한 소유입니다 라이브러리 있으며 ramda.js작성한 함수형 프로그래밍 라이브러리 입니다.

이 답변이 도움이 되길 바랍니다.


2

이런 식으로 할 수 있습니다

// @flow

const toPromise = (f: (any) => void) => {
  return new Promise<any>((resolve, reject) => {
    try {
      f((result) => {
        resolve(result)
      })
    } catch (e) {
      reject(e)
    }
  })
}

export default toPromise

그런 다음 사용하십시오

async loadData() {
  const friends = await toPromise(FriendsManager.loadFriends)

  console.log(friends)
}

2
이봐, 이것이 기존 답변에 무엇을 추가하는지 잘 모르겠습니다 (명확 할 수 있습니까?). 또한 promise 생성자 내에 try / catch가 필요하지 않습니다 (자동으로 수행). 이 기능이 어떤 기능을 수행하는지 명확하지 않습니다 (성공에 대한 단일 인수로 콜백을 호출합니까? 오류는 어떻게 처리됩니까?)
Benjamin Gruenbaum


1

내 약속 버전의 callback함수는 다음과 P같습니다.

var P = function() {
  var self = this;
  var method = arguments[0];
  var params = Array.prototype.slice.call(arguments, 1);
  return new Promise((resolve, reject) => {
    if (method && typeof(method) == 'function') {
      params.push(function(err, state) {
        if (!err) return resolve(state)
        else return reject(err);
      });
      method.apply(self, params);
    } else return reject(new Error('not a function'));
  });
}
var callback = function(par, callback) {
  var rnd = Math.floor(Math.random() * 2) + 1;
  return rnd > 1 ? callback(null, par) : callback(new Error("trap"));
}

callback("callback", (err, state) => err ? console.error(err) : console.log(state))
callback("callback", (err, state) => err ? console.error(err) : console.log(state))
callback("callback", (err, state) => err ? console.error(err) : console.log(state))
callback("callback", (err, state) => err ? console.error(err) : console.log(state))

P(callback, "promise").then(v => console.log(v)).catch(e => console.error(e))
P(callback, "promise").then(v => console.log(v)).catch(e => console.error(e))
P(callback, "promise").then(v => console.log(v)).catch(e => console.error(e))
P(callback, "promise").then(v => console.log(v)).catch(e => console.error(e))

P기능을 사용하려면 콜백 서명이이어야합니다 callback(error,result).


1
이것이 기본 약속이나 위의 답변보다 어떤 이점이 있습니까?
Benjamin Gruenbaum

네이티브 약속의 의미는 무엇입니까?
loretoparisi


아 물론입니다 :). 기본 아이디어를 보여주는 단지 예입니다. 실제로 네이티브 서명조차도 함수 서명이 정의 된 것과 같아야 (err, value) => ...하거나 사용자 정의 서명을 정의해야하는 방법을 알 수 있습니다 (사용자 정의 약속 된 함수 참조). 잘 부탁드립니다.
loretoparisi

1
@loretoparisi 참고로, var P = function (fn, ...args) { return new Promise((resolve, reject) => fn.call(this, ...args, (error, result) => error ? reject(error) : resolve(result))); };당신과 같은 일을 할 것이고 훨씬 간단합니다.
패트릭 로버츠 17 년

1

다음은 함수 (콜백 API)를 약속으로 변환하는 방법의 구현입니다.

function promisify(functionToExec) {
  return function() {
    var array = Object.values(arguments);
    return new Promise((resolve, reject) => {
      array.push(resolve)
      try {
         functionToExec.apply(null, array);
      } catch (error) {
         reject(error)
      }
    })
  }
}

// USE SCENARIO

function apiFunction (path, callback) { // Not a promise
  // Logic
}

var promisedFunction = promisify(apiFunction);

promisedFunction('path').then(()=>{
  // Receive the result here (callback)
})

// Or use it with await like this
let result = await promisedFunction('path');

-2

5 년 늦었지만 콜백 API에서 함수를 가져 와서 약속으로 바꾸는 promesify 버전을 여기에 게시하고 싶었습니다.

const promesify = fn => {
  return (...params) => ({
    then: cbThen => ({
      catch: cbCatch => {
        fn(...params, cbThen, cbCatch);
      }
    })
  });
};

이 매우 간단한 버전을 여기에서보십시오 : https://gist.github.com/jdtorregrosas/aeee96dd07558a5d18db1ff02f31e21a


1
그것은 약속이 아니며, 체인이 아니며, 콜백에서 발생하는 오류를 처리하거나 그때 두 번째 매개 변수를 수락합니다.
Benjamin Gruenbaum
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.