既存のコールバックAPIをpromiseに変換するにはどうすればよいですか?


721

promiseを操作したいのですが、次のような形式のコールバックAPIがあります。

1. DOMロードまたは他の1回限りのイベント:

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) {
            ...
        });
    });
});

PromiseでAPIを使用するにはどうすればよいですか、それを「約束する」にはどうすればよいですか?


私は自分の回答を投稿しましたが、特定のライブラリーまたはこれ以上の状況でこれを行う方法を拡張する回答と編集も大歓迎です。
Benjamin Gruenbaum 2014年

@Bergiこれは興味深いアイデアです。2つの一般的なアプローチ(Promiseコンストラクターと据え置きオブジェクト)を使用して一般的な答えを出そうとしました。私は答えに2つの選択肢を与えようとしました。RTFMingがこの問題を解決することに同意しますが、こことバグトラッカーの両方でこの問題に遭遇することが多いので、「標準的な質問」が設定されていると考えました-RTFMingはJSタグの問題の約50%を解決すると思います:Dもし回答に貢献したり、編集したりするための興味深い洞察があります。
Benjamin Gruenbaum

を作成new Promiseすると、大きなオーバーヘッドが発生しますか?Nodeアプリからすべての同期コードを削除するために、すべての同期Noje.js関数をPromiseでラップしたいのですが、これはベストプラクティスですか?つまり、静的な引数(文字列など)を受け入れ、計算結果を返す関数です。それをpromiseでラップする必要がありますか?... Nodejsに同期コードを含めるべきではないことをどこかで読みました。
ロニーロイストン

1
@RonRoystonいいえ、同期呼び出しをpromiseでラップすることはお勧めしません-I / Oを実行する可能性のある非同期呼び出しのみ
Benjamin Gruenbaum

回答:


744

プロミスには状態があり、それらは保留中として開始し、次のように解決できます。

  • 満たされたという意味は、計算が正常に完了したことです。
  • 拒否は、計算が失敗したことを意味します。

Promiseを返す関数はスローしないでください。代わりに拒否を返します。promiseのreturn関数からスローすると、両方を使用する必要があります。} catch { aの.catch。約束されたAPIを使用している人々は、約束がスローされることを期待していません。JSで非同期APIがどのように機能するかわからない場合は、まずこの回答参照してください。

1. DOMロードまたは他の1回限りのイベント:

したがって、Promiseを作成することは、通常、いつ決着するかを指定することを意味します。つまり、データが利用可能(およびでアクセス可能)であることを示すために、履行フェーズまたは却下フェーズに移行する時点を指定します.then

をサポートする最新のpromise実装 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;
}

またはjQueryのようなAPIを使用して、1回発生するイベントにフックします。

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 promiseのようなコンストラクターをサポートする最新のpromise実装では、次のようになります。

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"):

ノードスタイルのコールバック(ノードバック)には特定の形式があり、コールバックは常に最後の引数であり、その最初のパラメーターはエラーです。まず手動で1つを約束します。

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);
        });
    });
}

deferredsを使用すると、次のことができます(この例では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;   
}

一般に、手作業であまり多くのことを約束しないでください。Nodeを念頭に置いて設計されたPromiseライブラリのほとんどと、Node 8+のネイティブPromiseには、ノードバックを約束する組み込みのメソッドがあります。例えば

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

4.ノードスタイルのコールバックを含むライブラリ全体:

ここに黄金律はありません、あなたはそれらを一つずつ約束します。ただし、一部のpromise実装では、これを一括で行うことができます(Bluebirdなど)。ノードバックAPIをpromise APIに変換するのは次のように簡単です。

Promise.promisifyAll(API);

または、Nodeのネイティブpromiseを使用します。

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ハンドラーからpromiseを返すと、そのpromiseの値で解決または拒否されます。から投げる.thenハンドラーも良い習慣であり、約束を拒否します-これは有名な約束投げ安全です。
  • 実際にはonloadaddEventListenerではなくを使用する必要がありますonX

ベンジャミン、私は編集への招待を受け入れ、さらにjQueryの例をケース2に追加しました。表示するには、ピアレビューが必要です。あなたがそれを好き願っています。
Roamer-1888

@ Roamer-1888私は時間内にそれを見て受け入れなかったため、拒否されました。それだけの価値があるので、この追加は有用ではありますがあまり関連性があるとは思いません。
Benjamin Gruenbaum 2014

2
ベンジャミンは、再利用可能であるかどうかにかかわらずresolve()reject()私が提案した編集が関連していることを思い知らされます$.Deferred(fn)。jQueryの例が1つだけ含まれている場合はvar d = $.Deferred();、無視されがちな$.Deferred(fn)フォームを使用することを推奨する必要があるため、このフォームではなく、このフォームにすることをお勧めします。明らかにコンストラクタパターンを使用するライブラリ。
Roamer-1888 2014年

100%公正であるために、jQueryが許可することを知らなかったので$.Deferred(fn)、次の15分で既存の例の代わりにそれを編集すれば、時間通りにそれを承認することができると確信しています:)
Benjamin Gruenbaum

7
これは素晴らしい答えです。util.promisifyNode.jsがRC 8.0.0からコアに追加されることにも言及して、更新することができます。その動作はBluebird Promise.promisifyとそれほど変わりませんが、ネイティブのPromiseだけが必要な場合に備えて、追加の依存関係を必要としないという利点があります。このトピックについてもっと読みたい人のために、util.promisifyに関するブログ投稿を書きました。
ブルーノ

55

今日、私は使用することができますPromiseNode.jsプレーンなJavascriptの方法として。

PromiseKISSを使用したシンプルで基本的な例方法を使用):

プレーン Javascript非同期APIコード:

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

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

    successCallback( number / divider )

}

Promise JavaScript非同期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に使用してES7、プログラムフロー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) })

Promise 次のようなNode.jsに基づくプラットフォームでも使用できます。 react-native

ボーナスハイブリッドメソッド
(コールバックメソッドには、エラーと結果として2つのパラメーターがあると想定されます)

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でpromiseとして関数を変換する前

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) => { /** ... **/ });

Promiseのサポートの改善を参照してください


2
これを説明する回答はすでに2つあります。なぜ3つ目の回答を投稿するのですか
Benjamin Gruenbaum 2017年

1
ノードのそのバージョンがリリースされたからといって、「公式」機能の説明とリンクを報告しました。
Gian Marco Gherardi 2017年

14

Node.js 8.0.0のリリース候補には、あらゆる機能を約束する機能をカプセル化する新しいユーティリティutil.promisifyutil.promisifyについて書いた)があります。

他の回答で提案されているアプローチと大差ありませんが、コアメソッドであり、追加の依存関係を必要としないという利点があります。

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

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

次にreadFile、ネイティブを返すメソッドがありますPromise

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

1
ねえ、私(OP)は実際にutil.promisify2回提案しました(この質問が書かれた2014年、そして数か月前に私はNodeのコアメンバーとしてプッシュし、Nodeの現在のバージョンです)。まだ公開されていないため、この回答にはまだ追加していません。私たちは使用フィードバックを深く感謝し、リリースのためのより良いドキュメントを得るためにいくつかの落とし穴が何であるかを知るようになります:)
Benjamin Gruenbaum

1
さらに、util.promisifyあなたのブログ投稿で、約束のカスタムフラグについて話し合うこともできます:)
Benjamin

@BenjaminGruenbaum util.promisify.customシンボルを使用すると、util.promisifyの結果をオーバーライドできるということですか?正直なところ、これは意図的なミスでした。私はまだ有用なユースケースを見つけることができないためです。多分あなたは私にいくつかの入力を与えることができますか?
ブルーノ

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

昔ながらのバニラのjavaScriptを使用した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ライブラリには、promiseコールバック関数が含まれています。このような方法:

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リードでpromisifyingについてグーグルとして、これは有用であることが判明
エド・サイクス

4

コールバックを受け取る関数がいくつかあり、代わりにそれらがpromiseを返すようにしたい場合は、この関数を使用して変換を行うことができます。

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

promiseとasyncが組み込まれているノード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
    });

}

この例では、Promiseは失敗する理由がないため、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は、関数型プログラミングのための優れたライブラリです。上記のコードでは、apply(javascriptのようなfunction.prototype.apply)とappend(javascriptのようなfunction.prototype.push)を使用しました。したがって、コールバックスタイル関数をプロミススタイル関数に変換できます。

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

toPromisecheckErr関数はberserkライブラリが所有しており、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は必要ありません(これは自動的に行われます)。これが機能する関数も
わかり


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 2017年

ネイティブの約束はどういう意味ですか?
loretoparisi


はい、もちろんそうです:) 基本的なアイデアを示すための例です。実際には、ネイティブシグネチャでも関数シグネチャを定義する必要があるか(err, value) => ...、カスタムシグネチャを定義する必要があるかを確認できます(カスタムの約束された関数を参照)。よろしくお願いします。
loretoparisi 2017年

1
@loretoparisi FYIはvar P = function (fn, ...args) { return new Promise((resolve, reject) => fn.call(this, ...args, (error, result) => error ? reject(error) : resolve(result))); };あなたのものと同じことをするでしょう、そしてそれはずっと簡単です。
Patrick Roberts、

1

以下は、関数(コールバックAPI)をpromiseに変換する方法の実装です。

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から関数を取り、それらをpromiseに変換する私のpromesifyバージョンをここに投稿したいと思いました

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

こちらの非常にシンプルなバージョンをご覧くださいhttps : //gist.github.com/jdtorregrosas/aeee96dd07558a5d18db1ff02f31e21a


1
それは約束ではありません。連鎖せず、コールバックでスローされたエラーに対処するか、そのときに2番目のパラメーターを受け入れます...
Benjamin Gruenbaum
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.