简体   繁体   中英

How to convert JavaScript functions that use multiple callbacks, to use promises?

I am trying to convert the way I use asynchronous functions from callbacks to promises.

I understand this basic conversion shown here, where callbacks are converted to the resolve and reject functions:

// CALLBACK
const getData = (id, callback) => {
    setTimeout(() => {
        if (!id) return callback('ERROR: id is missing')
        return callback("The data for id " + id);
    }, 1000)
};

getData(111, console.log);
getData(222, console.log);
getData(null, console.log);

// PROMISE
const getData2 = id => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (!id) reject('id is missing');
            resolve("The data for id " + id);
        }, 1000);
    });
};
getData2(333).then(console.log).catch((message) => console.log("ERROR: " + message));
getData2(444).then(console.log).catch((message) => console.log("ERROR: " + message));
getData2(null).then(console.log).catch((message) => console.log("ERROR: " + message));

But I often use callbacks as in the following scenerio where you can have a process which takes a long time and sends out bits of data in numerous callbacks back to the calling code as it processes its information:

sleep = function (ms) {
    var start = new Date().getTime();
    let now = 0;
    let difference = 0;
    for (var i = 0; i < 1e17; i++) {
        now = new Date().getTime();
        difference = now - start;
        if (difference > ms) {
            break;
        }
    }
}

const goShopping = (list, cbItemReport, cbFinished, cbError) => {
    let count = 0;
    let numberFound = 0;

    const randomError = Math.floor(Math.random() * 3);
    if (randomError == 0) {
        cbError('Something went wrong, trip aborted.');
    } else {
        list.forEach(item => {
            const randomFound = Math.floor(Math.random() * 4);
            if (randomFound > 0) {
                cbItemReport(item, true, ++count);
                numberFound++;
            } else {
                cbItemReport(item, false, ++count);
            }
            sleep(1000);
        })
        cbFinished(`Bought ${numberFound} things.`);
    }
}

goShopping(['milk', 'eggs', 'sugar', 'bread'],
    (item, found, count) => {
        console.log(`Item #${count} "${item}" was ${found ? 'found' : 'not found'}.`);
    },
    (message) => {
        console.log("Returned from shopping: " + message);
    },
    (error) => {
        console.log("ERROR: " + error);
    });

How would I convert this latter use of callbacks to promises? In this case, the three callbacks cbItemReport , cbFinished , cbError are too many to map to the two that Promise has, ie only resolve (cbFinished) and reject (cbError), or what am I missing here?

From what it sounds like, you are looking to implement something like the RxJs library, so why not just use it?

Check out RxJs here

For example a call could look like this then:

const sub = new Subject(); // Create a subject 

sub.asObservable().subscribe( // Subscribe to that subject as obserable
    ({item, found, count}) => { // next
        console.log(`Item #${count} "${item}" was ${found ? 'found' : 'not found'}.`);
    },
    error => { // error
        console.log("ERROR: " + error);
    },
    message => { // complete
        console.log("Returned from shopping: " + message);
    }
);

const goShopping = (list) => {
    let count = 0;
    let numberFound = 0;

    const randomError = Math.floor(Math.random() * 3);
    if (randomError == 0) {
        sub.error('Something went wrong, trip aborted.'); // push an error to the subject
    } else {
        list.forEach(item => {
            const randomFound = Math.floor(Math.random() * 4);
            if (randomFound > 0) {
                sub.next({item: item, found: true, count: ++count}); // push a result to subject (single object)
                numberFound++;
            } else {
                sub.next({item: item, found: true, count: ++count}); // same as above
            }
            sleep(1000);
        })
        sub.complete(`Bought ${numberFound} things.`); // push complete to subject. after that no next is allowed anymore
    }
}

goShopping(['milk', 'eggs', 'sugar', 'bread'])

Call resolve from cbFinished , reject from cbError and write a cbItemReport that does whatever you want it to do to each item, just as in the existing code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM