Design pattern for managing multiple asynchronous JavaScript operations

前端 未结 3 1499
暖寄归人
暖寄归人 2020-12-12 16:05

I\'m looking for a good design pattern for keeping track of a bunch of different asynchronous JavaScript activities (images loading, multiple AJAX calls, sequenced AJAX call

3条回答
  •  自闭症患者
    2020-12-12 16:23

    With promises now standard in ES6 and many good promise libraries extending that for both new features and backwards compatibility, it seems that promises are the way to go here.

    The solution starts with taking each async operation and creating a wrapper that returns a promise:

    For loading an image:

    function loadImage(url) {
        return new Promise(function(resolve, reject) {
            var img = new Image();
            img.onload = function() {
                resolve(img);
            };
            img.onerror = img.onabort = function() {
                reject(url);
            };
            img.src = url;
        });
    }
    

    For making an Ajax call (simplified version):

    function ajaxGet(url) {
        return new Promise(function(resolve, reject) {
            var req = new XMLHttpRequest();
            req.addEventListener("load", resolve);
            req.addEventListener("error", reject);
            req.addEventListener("abort", reject);
            req.open("GET", url);
            req.send();
        });
    }
    

    For waiting a particular amount of time before executing an operation, you can even making a promise version of setTimeout() so it can be chained with other promise operations:

    // delay, return a promise
    // val is optional
    function delay(t, val) {
        return new Promise(function(resolve) {
            setTimeout(function() {
                resolve(val);
            }, t);
        });
    }
    

    Now, these can be combined to create the logic the question asked for:

    There are three images loading. As soon as one specific image is loaded, I want to display it. Once it's been displayed for a certain amount of time, I want to display the second image. The third one goes in a queue for later display.

    // start all three images loading in parallel, get a promise for each one
    var imagePromises = [url1, url2, url3].map(function(item) {
        return loadImage(item);
    });
    
    // display the three images in sequence with a delay between them
    imagePromises.reduce(function(p, item) {
        return p.then(function() {
            // when the next image is ready display it
            return item.then(function(img) {
                displayImage(img);
                return delay(15 * 1000);
            });
        });
    }, Promise.resolve());
    

    This use of .reduce() shows a classic design pattern for sequencing a series of operations on an array using promises.

    There are three AJAX calls that must happen in consecutive order (the output of one is used as part of the input of the next).

    and

    When the AJAX calls are done, there's a bunch of JS processing of the results to do and then two more images need to get loaded.

    var p = ajaxGet(url1).then(function(results1) {
        // do something with results1
        return ajaxGet(url2);
    }).then(function(results2) {
        // do something with results2
        return ajaxGet(url3); 
    }).then(function(results3) {
        // process final results3 here
        // now load two images and when they are loaded do some display stuff
        return Promise.all(loadImage(imgx), loadImage(imgy)).then(function(imgs) {
            doSomeDisplayStuff(imgs);
        });
    });
    

提交回复
热议问题