Promise

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

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

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

观察上述代码执行,在Chrome的控制台输出可以看到:

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

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

AJAX就是典型的异步操作。以上一节的代码为例:

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

把回调函数success(request.responseText)fail(request.status)写到一个AJAX操作里很正常,但是不好看,而且不利于代码复用。

有没有更好的写法?比如写成这样:

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

这种链式写法的好处在于,先统一执行AJAX逻辑,不关心如何处理结果,然后,根据结果是成功还是失败,在将来的某个时候调用success函数或fail函数。

古人云:“君子一诺千金”,这种“承诺将来会执行”的对象在JavaScript中称为Promise对象。

Promise有各种开源实现,在ES6中被统一规范,由浏览器直接支持。先测试一下你的浏览器是否支持Promise:

'use strict';

new Promise(function () {});

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

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

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

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

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

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

// 如果成功,执行这个函数:
p1.then(function (result) {
    console.log('成功:' + result);
});

test函数执行失败时,我们告诉Promise对象:

p2.catch(function (reason) {
    console.log('失败:' + reason);
});

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

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

实际测试一下,看看Promise是如何异步执行的:

'use strict';

// 清除log:
var logging = document.getElementById('test-promise-log');
while (logging.children.length > 1) {
    logging.removeChild(logging.children[logging.children.length - 1]);
}

// 输出log到页面:
function log(s) {
    var p = document.createElement('p');
    p.innerHTML = s;
    logging.appendChild(p);
}

Log:

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

promise

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

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

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

其中,job1job2job3都是Promise对象。

下面的例子演示了如何串行执行一系列需要异步计算获得结果的任务:

'use strict';

var logging = document.getElementById('test-promise2-log');
while (logging.children.length > 1) {
    logging.removeChild(logging.children[logging.children.length - 1]);
}

function log(s) {
    var p = document.createElement('p');
    p.innerHTML = s;
    logging.appendChild(p);
}

Log:

setTimeout可以看成一个模拟网络等异步执行的函数。现在,我们把上一节的AJAX异步执行函数转换为Promise对象,看看用Promise如何简化异步处理:

'use strict';

// ajax函数将返回Promise对象:
function ajax(method, url, data) {
    var request = new XMLHttpRequest();
    return new Promise(function (resolve, reject) {
        request.onreadystatechange = function () {
            if (request.readyState === 4) {
                if (request.status === 200) {
                    resolve(request.responseText);
                } else {
                    reject(request.status);
                }
            }
        };
        request.open(method, url);
        request.send(data);
    });
}
Result:

除了串行执行若干异步任务外,Promise还可以并行执行异步任务。

试想一个页面聊天系统,我们需要从两个不同的URL分别获得用户的个人信息和好友列表,这两个任务是可以并行执行的,用Promise.all()实现如下:

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

有些时候,多个异步任务是为了容错。比如,同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可。这种情况下,用Promise.race()实现:

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
});

由于p1执行较快,Promise的then()将获得结果'P1'p2仍在继续执行,但执行结果将被丢弃。

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

版权声明:本文为博主原创文章,欢迎任何形式的转载,转载请注明出处。https://blog.csdn.net/mrchengzp/article/details/78573208假设高度已知,请写出三栏布局,左栏、右栏宽度300px,中间宽度自适应。  这道题本身的难度并不大,我们在布局页面的时候,写个三栏布局还是挺简单的。但是如果在面试的时候遇到这道题,就没有那么简单了。看似简单的一道题,想把它答好是不简单的。往往越简单的题越不好答。如果看到这题只想到了浮动和绝对定位,那这题你连及格都及格不了。
下面是5种三栏布局的方法。 在写布局代码之前,先写两段公共的样式,此段写在头部。
样式<style media="screen">      html *{        padding: 0;        margin: 0;      }      .layout article div{        min-height: 100px;      }    </style>1234567891. 浮动布局<!--浮动布局  -->    <section class="layout float">      <style media="screen">      .layout.float .left{        float:left;        300px;        background: red;      }      .layout.float .center{        background: yellow;      }      .layout.float .right{        float:right;        300px;        background: blue;      }      </style>      <h1>三栏布局</h1>      <article class="left-right-center">        <div class="left"></div>        <div class="right"></div>        <div class="center">          <h2>浮动解决方案</h2>          1.这是三栏布局的浮动解决方案;          2.这是三栏布局的浮动解决方案;        </div>      </article>    </section>12345678910111213141516171819202122232425262728浮动布局是有局限性的,浮动元素是脱离文档流,要做清除浮动,这个处理不好的话,会带来很多问题,比如高度塌陷等。 浮动布局的优点就是比较简单,兼容性也比较好。只要清除浮动做的好,是没有什么问题的。 延伸:你知道哪些清除浮动的方案?每种方案的有什么优缺点?
2.绝对定位布局<!-- 绝对布局 -->    <section class="layout absolute">      <style>        .layout.absolute .left-center-right>div{          position: absolute;        }        .layout.absolute .left{          left:0;          300px;          background: red;        }        .layout.absolute .center{          left: 300px;          right: 300px;          background: yellow;        }        .layout.absolute .right{          right:0;          300px;          background: blue;        }      </style>      <h1>三栏布局</h1>      <article class="left-center-right">        <div class="left"></div>        <div class="center">          <h2>绝对定位解决方案</h2>          1.这是三栏布局的绝对定位解决方案;          2.这是三栏布局的绝对定位解决方案;        </div>        <div class="right"></div>      </article>    </section>123456789101112131415161718192021222324252627282930313233绝对定位布局优点,很快捷,设置很方便,而且也不容易出问题,你可以很快的就能想出这种布局方式。 缺点就是,绝对定位是脱离文档流的,意味着下面的所有子元素也会脱离文档流,这就导致了这种方法的有效性和可使用性是比较差的。
3.flex布局<!-- flexbox布局 -->    <section class="layout flexbox">      <style>        .layout.flexbox{          margin-top: 110px;        }        .layout.flexbox .left-center-right{          display: flex;        }        .layout.flexbox .left{          300px;          background: red;        }        .layout.flexbox .center{          flex:1;          background: yellow;        }        .layout.flexbox .right{          300px;          background: blue;        }      </style>      <h1>三栏布局</h1>      <article class="left-center-right">        <div class="left"></div>        <div class="center">          <h2>flexbox解决方案</h2>          1.这是三栏布局的felx解决方案;          2.这是三栏布局的flex解决方案;        </div>        <div class="right"></div>      </article>    </section>123456789101112131415161718192021222324252627282930313233felxbox布局是css3里新出的一个,它就是为了解决上述两种方式的不足出现的,是比较完美的一个。目前移动端的布局也都是用flexbox。 felxbox的缺点就是不能兼容IE8及以下浏览器。
4.表格布局<!-- 表格布局 -->    <section class="layout table">      <style>        .layout.table .left-center-right{          100%;          height: 100px;          display: table;        }        .layout.table .left-center-right>div{          display: table-cell;        }        .layout.table .left{          300px;          background: red;        }        .layout.table .center{          background: yellow;        }        .layout.table .right{          300px;          background: blue;        }      </style>      <h1>三栏布局</h1>      <article class="left-center-right">        <div class="left"></div>        <div class="center">          <h2>表格布局解决方案</h2>          1.这是三栏布局的表格解决方案;          2.这是三栏布局的表格解决方案;        </div>        <div class="right"></div>      </article>    </section>12345678910111213141516171819202122232425262728293031323334表格布局在历史上遭到很多人的摒弃,说表格布局麻烦,操作比较繁琐,其实这是一种误解,在很多场景中,表格布局还是很适用的,比如这个三栏布局,用表格布局就轻易写出来了。还有表格布局的兼容性很好,在flex布局不兼容的时候,可以尝试表格布局。 表格布局也是有缺陷的,当其中一个单元格高度超出的时候,两侧的单元格也是会跟着一起变高的,而有时候这种效果不是我们想要的。
5.网格布局<!-- 网格布局 -->    <section class="layout grid">      <style>        .layout.grid .left-center-right{          100%;          display: grid;          grid-template-rows: 100px;          grid-template-columns: 300px auto 300px;        }        .layout.grid .left-center-right>div{
        }        .layout.grid .left{          300px;          background: red;        }        .layout.grid .center{          background: yellow;        }        .layout.grid .right{
          background: blue;        }      </style>      <h1>三栏布局</h1>      <article class="left-center-right">        <div class="left"></div>        <div class="center">          <h2>网格布局解决方案</h2>          1.这是三栏布局的网格布局解决方案;          2.这是三栏布局的网格布局解决方案;        </div>        <div class="right"></div>      </article>    </section>1234567891011121314151617181920212223242526272829303132333435网格布局也是新出的一种布局方式,如果你答出这种方式,也就证明了你的实力,证明你对技术热点是有追求的,也说明你有很强的学习能力。
效果图 
--------------------- 作者:程序员的程 来源:CSDN 原文:https://blog.csdn.net/mrchengzp/article/details/78573208 版权声明:本文为博主原创文章,转载请附上博文链接!

原文地址:https://www.cnblogs.com/Yanss/p/10145828.html