817

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 を使用するにはどうすればよいですか? API を「約束する」にはどうすればよいですか?

4

24 に答える 24

830

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

  • 計算が正常に完了したことを意味します。
  • rejectは、計算が失敗したことを意味します。

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

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

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

Promiseネイティブ ES6 promise のようなコンストラクターをサポートする最新の promise 実装では:

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

次に、結果のプロミスを次のように使用します。

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

deferred をサポートするライブラリの場合 (この例では $q を使用しますが、後で jQuery も使用します):

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

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

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

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

deferred をサポートするライブラリ (ここではこの例では 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 の deferredメソッド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);
        });
    });
}

deferred を使用すると、次のことができます (この例では 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 8+ のネイティブ Promise と同様に、Node を念頭に置いて設計されたほとんどの Promise ライブラリには、ノードバックを Promisify するためのメソッドが組み込まれています。例えば

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

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

ここには黄金律はありません。1 つずつ約束します。ただし、いくつかの promise 実装では、これを一括で実行できます。たとえば、Bluebird では、nodeback 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ハンドラーにいるときは、約束する必要はありません。ハンドラーから promise を返す.thenと、その promise の値で解決または拒否されます。ハンドラーから.thenスローすることも良い習慣であり、promise を拒否します。これは、有名なプロミス スロー セーフティです。
  • 実際には、ではなくonloadを使用する必要があります。addEventListeneronX
于 2014-03-19T22:47:26.387 に答える
23

window.onload@Benjamin による提案は、ロード後に呼び出されたかどうかを検出しないため、常に機能するとは思いません。それに何度も噛まれました。常に動作するはずのバージョンを次に示します。

function promiseDOMready() {
    return new Promise(function(resolve) {
        if (document.readyState === "complete") return resolve();
        document.addEventListener("DOMContentLoaded", resolve);
    });
}
promiseDOMready().then(initOnLoad);
于 2015-01-14T04:15:27.270 に答える
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);
});
于 2016-11-28T03:07:12.177 に答える
7

Node JS で JavaScript ネイティブ promise を使用できます。

私の 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
于 2016-06-20T13:38:56.970 に答える
3

Node.js 8では、この npm モジュールを使用してオンザフライでオブジェクト メソッドを約束できます。

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

オブジェクトが変更されないように、 util.promisifyProxiesを使用します。メモ化も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);
    // ...
  });

nativecallを使用しapplyて、いくつかのコンテキストをバインドすることもできます。

doAsync(myFunc).apply(context, params)
  .then(result => { /*...*/ });
于 2017-10-12T22:19:07.037 に答える
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()呼び出されることはありません。

于 2017-01-22T13:22:52.753 に答える
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)
}
于 2018-10-09T13:35:18.520 に答える
-2

5 年ほど遅れていますが、コールバック API から関数を取得してプロミスに変換する promesify バージョンをここに投稿したいと思います。

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

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

于 2019-04-04T21:55:37.857 に答える