기존 콜백 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에서 일반적입니다. 가지고있는 일반적인 경우에서 살펴 보자 onSuccess과 onFail:

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 지연 resolve및 reject메소드가 "분리 가능" 하다는 사실을 이용합니다 . 즉. 그것들은 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\await에 ES7A의 프로그램 흐름 대기하기 위해 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)
    )

toPromise 및 checkErr 기능은 맹렬한 소유입니다 라이브러리 있으며 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.