Promise的一些相关讲解

在javascrpit的语言特性上 有明确的一个特性指出,该语言的是单线程进程。这就意味着JavaScript的所有网络操作,浏览器事件,都必须是异步执行。

如下面的例子,可以感受到单线程与异步回调:

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

将上面代码在控制台打出 我们可以看的到

before setTimeout()
after setTimeout()
(等待1秒后)
Done

可以看出,异步操作 会在未来的某个点 由回调函数触发

再来看一个典型的异步操作:ajax请求

// 这个是原生的ajax请求一部分  注册事件 onreadystatechange 状态改变就会调用
request.onreadystatechange = function () {
    if (request.readyState === 4) { //判断服务器是否响应正确
        if (request.status === 200) {
            return success(request.responseText);
        } else {
            return fail(request.status);
        }
    }
}
// 另外 附上原生的ajax请求
//步骤一:创建异步对象
var ajax = new XMLHttpRequest();
//步骤二:设置请求的url参数,参数一是请求的类型,参数二是请求的url,可以带参数,动态的传递参数starName到服务端
ajax.open('get','getStar.php?starName='+name);
//步骤三:发送请求
ajax.send();
//步骤四:注册事件 onreadystatechange 状态改变就会调用
ajax.onreadystatechange = function () {
if (ajax.readyState==4 &&ajax.status==200) {
//步骤五 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的页面是存在的
    console.log(ajax.responseText);//输入相应的内容
  }
}

对于上面的代码 有些繁琐,可以改写嘛?比如这样

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

 上面的代码 体现了一种链式写法,这种写法看起来比较简洁些,并且还体现了另外一个思想:

我不管你有没有请求成功,但是我发起了与服务器的通信,至于结果,我可以再在未来的某个代码片段里去调用 success 与 fail 的函数。

于是 这种 " 承诺于未来将会执行 " 的对象,被称为了promise  中文含义 : 承诺。

promise 有很多的开源实现 在ES6中被统一规范,由浏览器直接支持。可以测试你的浏览器是否支持promise。在控制台上打印以下代码:

new Promise(function () {
   console.log('我支持promise') 
})

 既然知道了promise的大概含义,我们来实现一个这样的例子吧:下面代码里 我们默认当num<1时 是通信成功,不然就是通信失败。模拟一个ajax请求。

        function test(success,fail) {
            var num = Math.random() * 2;
            console.log('相应时间为'+ num +'秒')
            setTimeout(function () {
                if(num < 1) {
                    success('执行成功函数');
                }else {
                    fail('执行失败的函数')
                }
            },num * 1000)
        }

能够看出 , test()函数有两个参数,其实就是回调函数,当num < 1时 我们定义其为相应成功,调用success函数,失败时 就调用 fail 函数。由此可以看出:

test()函数只在关心自身的逻辑,并没有去关心 执行success 还是 fail 函数 如何去处理结果。

于是 我们可以用promise对象 来出来这个执行的函数。

        var p1 = new Promise(test);
        var p2 = p1.then(function (data) {
            console.log('成功' + data)
        });
        var p3 = p2.catch(function (data2) {
            console.log('失败' + data2)
        })

变量p1是一个Promise对象,它负责执行test函数。由于test函数在内部是异步执行的,当test函数执行成功时,我们告诉Promise对象:

如果执行成功 则执行

p1.then(function (data) {
            console.log('成功' + data)
        });

失败 则执行:

p2.catch(function (data2) {
            console.log('失败' + data2)
        })

Promise对象可以串联起来,所以上述代码可以简化为:

new Promise(test).then(function (data) {
    console.log('成功:' + data);
}).catch(function (data2) {
    console.log('失败:' + data2);
});

继续上面的栗子 看看promise是如何实现异步执行的:

new Promise(function (success, fail) {
    console.log('start new Promise...');
    var timeOut = Math.random() * 2;
    console.log('set timeout to: ' + timeOut + ' seconds.');
    setTimeout(function () {
        if (timeOut < 1) {
            console.log('执行成功函数');
            success('200 OK');
        }
        else {
            console.log('执行失败函数');
            fail('timeout in ' + timeOut + ' seconds.');
        }
    }, timeOut * 1000);
}).then(function (r) {
    console.log('Done: ' + r);
}).catch(function (reason) {
    console.log('Failed: ' + reason);
});

可以将上面的代码打入控制台  你可以看到类如下面的结果

可见Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了

此时 我们再看一个axios的请求  axios支持promise API

axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

怎么样 形式上 是不是就会感觉到 一种很熟悉的,这样也就能理解 axios请求的写法了。

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

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

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

下面有个具体的例子说明:

// 0.5秒后返回input*input的计算结果:
function multiply(input) {
    return new Promise(function (resolve, reject) {
        console.log('计算 ' + input + ' x ' + input + '...');
        setTimeout(resolve, 500, input * input);
    });
}

// 0.5秒后返回input+input的计算结果:
function add(input) {
    return new Promise(function (resolve, reject) {
        console.log('计算 ' + input + ' + ' + input + '...');
        setTimeout(resolve, 500, input + input);
    });
}

var p = new Promise(function (resolve, reject) {
    console.log('开始promise');
    resolve(123);
});

p.then(multiply)
 .then(add)
 .then(multiply)
 .then(add)
 .then(function (result) {
    console.log('最后的结果 ' + result);
});
View Code

在控制台打出上面的代码 可以看到;

我们可以再看一下 vuejs 官方提供的与服务器通信的插件 axios 的大概实现原理。这里是基于原生ajax请求改写

function ajax(method,url,params) {
                var request = new XMLHttpRequest();
                return new Promise(function (success,fail) {
                    request.onreadystatechange = function () {
                        if(request.readyState === 4 && request.status === 200) {
                            success(request.responseText);
                        }else {
                            fail(request.status)
                        }
                    }
                    request.open(method,url);
                    request.send(params)
                })
            }

紧接着 我们的调用

var p = ajax('GET', '/api',params);
p.then(function (text) { // 如果AJAX成功,获得响应内容
    console.log(text)
}).catch(function (status) { // 如果AJAX失败,获得响应代码
    lconsole.log(status);
});

另外:

promise.all()方法,可以支持并行异步操作

var p1 = new Promise(function (resolve, reject) {
                setTimeout(resolve, 500, '第一个');
            });
            var p2 = new Promise(function (resolve, reject) {
                setTimeout(resolve, 600, '第二个');
            });
            // 同时执行p1和p2,并在它们都完成后执行then:
            Promise.all([p1, p2]).then(function (results) {
                console.log(results); // 获得一个Array: ['第一个', '第二个']
            });

Promise.race()方法,可以实现两个异步请求,有一个先返回即可

            var p1 = new Promise(function (resolve, reject) {
                setTimeout(resolve, 500, '第一个');
            });
            var p2 = new Promise(function (resolve, reject) {
                setTimeout(resolve, 600, '第二个');
            });
            // 同时执行p1和p2,并在它们都完成后执行then:
            Promise.race([p1, p2]).then(function (results) {
                console.log(results); // 输出第一个 第二个会被默认丢弃
            });
            

由于p1运行较快,所以promise的then 会获得 ‘第一个’的输出。第二个会被默认丢弃。

综上:

如果我们组合使用Promise,就可以把很多异步任务以并行和串行的方式组合起来执行 

猜你喜欢

转载自www.cnblogs.com/mamimi/p/9446594.html
今日推荐