Promise

作者: whitsats | 来源:发表于2017-07-31 13:57 被阅读0次

    Promise

    在JavaScript的世界中,所有代码都是单线程执行的。

    由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现:

    function callback() {
        console.log('Done');
    }
    console.log('before setTimeout()');
    setTimeout(callback, 1000); // 1秒钟后调用callback函数
    console.log('after setTimeout()')
    

    异步操作会在将来的某个时间点触发一个函数调用。

    AJAX就是典型的异步操作.

    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                return success(xhr.responseText);
            } else {
                return fail(xhr.status);
            }
        }
    }
    

    更好的写法可能是:

    var ajax = ajaxGet('http://...');
    ajax.ifSuccess(success)
        .ifFail(fail);
    

    这种链式写法的好处在于,先统一执行AJAX逻辑,不关心如何处理结果,然后,根据结果是成功还是失败,在将来的某个时候调用success函数或fail函数。
    古人云:“君子一诺千金”,这种“承诺将来会执行”的对象在JavaScript中称为Promise对象。

    ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。

    下面代码创造了一个Promise实例。

    var promise = new Promise(function(resolve, reject) {
      // ... some code
    
      if (/* 异步操作成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });
    

    Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

    resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

    Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数。

    promise.then(function(value) {
      // success
    }, function(error) {
      // failure
    });
    

    then方法可以接受两个回调函数作为参数,分别是成功和失败的回调函数。其中,第二个回调函数是可选参数。我们可以用catch方法接受失败的回调。

    我们先看一个最简单的Promise例子:生成一个0-2之间的随机数,如果小于1,则等待一段时间后返回成功,否则返回失败:

    function test(resolve, reject) {
        var timeOut = Math.random() * 2;
        console.log('set timeout to: ' + timeOut + ' seconds.');
        setTimeout(function () {
            if (timeOut < 1) {
                console.log('call resolve()...');
                resolve('200 OK');
            }
            else {
                console.log('call reject()...');
                reject('timeout in ' + timeOut + ' seconds.');
            }
        }, timeOut * 1000);
    }
    

    这个test()函数有两个参数,这两个参数都是函数,如果执行成功,我们将调用resolve('200 OK'),如果执行失败,我们将调用reject('timeout in ' + timeOut + ' seconds.')。可以看出,test()函数只关心自身的逻辑,并不关心具体的resolve和reject将如何处理结果。

    有了执行函数,我们就可以用一个Promise对象来执行它,并在将来某个时刻获得成功或失败的结果:

    new Promise(function (resolve, reject) {
        log('start new Promise...');
        var timeOut = Math.random() * 2;
        console.log('set timeout to: ' + timeOut + ' seconds.');
        setTimeout(function () {
            if (timeOut < 1) {
                console.log('call resolve()...');
                resolve('200 OK');
            }
            else {
                console.log('call reject()...');
                reject('timeout in ' + timeOut + ' seconds.');
            }
        }, timeOut * 1000);
    }).then(function (r) {
        console.log('Done: ' + r);
    }).catch(function (reason) {
        console.log('Failed: ' + reason);
    });
    
    

    Promise还可以做更多的事情,比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续并执行错误处理函数。

    要串行执行这样的异步任务,不用Promise需要写一层一层的嵌套代码。有了Promise,我们只需要简单地写:

    job1.then(job2).then(job3).catch(handleError)
    

    例如:

        function multiply(input) {
            return new Promise(function(resolve, reject) {
                console.log('calculating ' + input + ' x ' + input + '...');
                if (Math.random()*5>1) {
                    setTimeout(resolve, 500, input * input);
                }else{
                    reject('第二步出错')
                }
            });
        }
    
        // 0.5秒后返回input+input的计算结果:
        function add(input) {
            return new Promise(function(resolve, reject) {
                console.log('calculating ' + input + ' + ' + input + '...');
                if (Math.random()*5>1) {
                    setTimeout(resolve, 500, input + input);
                }else{
                    reject('第三步出错')
                }
            });
        }
    
        var p = new Promise(function(resolve, reject) {
            console.log('start new Promise...');
            if (Math.random()*5>1) {
                resolve(123);
            }else{
                reject('第一步出错')
            }
        });
    
        p.then(multiply)
            .then(add)
            .then(multiply)
            .then(add)
            .then(function(result) {
                console.log('Got value: ' + result);
            }).catch(function(msg){
                console.log(msg)
            });
    

    同时,Promise还允许并行执行异步任务。

    试想一个页面聊天系统,我们需要从两个不同的URL分别获得用户的个人信息和好友列表,这两个任务是可以并行执行的,用Promise.race()载入多个异步,只取值最先返回的结果,后返回的结果会被直接丢弃

    相关文章

      网友评论

        本文标题:Promise

        本文链接:https://www.haomeiwen.com/subject/hnxrlxtx.html