金沙棋牌官方平台

当前位置:金沙棋牌 > 金沙棋牌官方平台 > 流程控制,异步流程控制

流程控制,异步流程控制

来源:http://www.logblo.com 作者:金沙棋牌 时间:2019-11-22 11:08

什么是 Headless Chrome

Headless Chrome 是 Chrome 浏览器的无分界面形态,可以在不伸开浏览器的前提下,使用具备 Chrome 援救的性状运营你的次序。比较于现代浏览器,Headless Chrome 越发便利测验web 应用,获得网址的截图,做爬虫抓取音信等。相比于出道较早的 PhantomJS,SlimerJS 等,Headless Chrome 则更进一层左近浏览器情形。

丑陋的 try/catch

假诺实践停业的 await 未有包装 try / catchasync 函数将静默退出。如若有一长串异步 await 命令,须求四个 try / catch 包裹。

取代方案是采取高阶函数来捕捉错误,不再须求 try / catch 了(感谢@wesbos的建议):

async function connect() { const connection = await asyncDBconnect(''), session = await asyncGetSession(connection), user = await asyncGetUser(session), log = await asyncLogAccess(user); return true; } // 使用高阶函数捕获错误 function catchErrors(fn) { return function (...args) { return fn(...args).catch(err => { console.log('E兰德宝马X5RO翼虎', err); }); } } (async () => { await catchErrors(connect)(); })();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
async function connect() {
 
  const
    connection = await asyncDBconnect('http://localhost:1234'),
    session = await asyncGetSession(connection),
    user = await asyncGetUser(session),
    log = await asyncLogAccess(user);
 
  return true;
}
 
// 使用高阶函数捕获错误
function catchErrors(fn) {
  return function (...args) {
    return fn(...args).catch(err => {
      console.log('ERROR', err);
    });
  }
}
 
(async () => {
  await catchErrors(connect)();
})();

当使用必得回到差别于其余的荒谬时,这种作法就不太实用了。

固然有一点欠缺,async/await 照旧 JavaScript 特别管用的补偿。越来越多财富:

  • MDN async 和 await
  • 异步函数 – 升高 Promise 的易用性
  • TC39 异步函数标准
  • 用异步函数简化异步编码

并发伏乞

聊到底同意气风发域名下能够并发四个 HTTP 伏乞,对于这种无需按顺序加载,只需求按梯次来处理的产出必要,Promise.all 是最棒的解决办法。因为Promise.all 是原生函数,大家就引述文书档案来解释一下。

Promise.all(iterable) 方法指当全部在可迭代参数中的 promises 已做到,大概第贰个传递的 promise(指 reject卡塔尔国战败时,重回 promise。
出自 Promise.all() – JavaScript | MDN

那我们就把demo第11中学的例子改一下:

JavaScript

const promises = urls.map(loadImg) Promise.all(promises) .then(imgs => { imgs.forEach(addToHtml) document.querySelector('.loading').style.display = 'none' }) .catch(err => { console.error(err, 'Promise.all 当此中一个产出谬误,就会reject。') })

1
2
3
4
5
6
7
8
9
const promises = urls.map(loadImg)
Promise.all(promises)
  .then(imgs => {
    imgs.forEach(addToHtml)
    document.querySelector('.loading').style.display = 'none'
  })
  .catch(err => {
    console.error(err, 'Promise.all 当其中一个出现错误,就会reject。')
  })

demo2地址:现身乞请 – Promise.all

题目二

怎样拿到 Headless Chrome

目前,Mac 上 Chrome 59 beta 版本与 Linux 上的 Chrome 57+ 已经发轫扶持 headless 天性。Windows 上 Chrome 临时不帮衬,能够应用 Chrome Canary 60 实行付出。

单线程管理程序

JavaScript 是单线程的。当浏览器选项卡施行脚本时,别的具备操作都会停下。那是自然的,因为对页面 DOM 的变动不能并发实行;叁个线程
重定向 UWranglerL 的还要,另八个线程正要增添子节点,这么做是人命关天的。

顾客不便于觉察,因为管理程序会以组块的花样神速实施。举个例子,JavaScript 检验到开关点击,运营总结,并更改DOM。大器晚成旦产生,浏览器就能够私行管理队列中的下一个体系。

(附注: 此外语言举个例子 PHP 也是单线程,可是通过八线程的服务器比方 Apache 管理。同黄金时代 PHP 页面相同的时候提倡的五个央浼,能够运维四个线程运转,它们是并行隔绝的 PHP 实例。)

Promise 异步流程序调节制

2017/10/04 · JavaScript · Promise

原作出处: 麦子谷   

})

长途调整

在上文中描述的都应用终端命令运行 Headless Chrome,下文以获得截图为例,尝试什么在前后相继里决定 Headless Chrome。

安装正视

JavaScript

npm install lighthouse chrome-remote-interface --save

1
npm install lighthouse chrome-remote-interface --save

兑现截图的大约思路为:通过选用 lighthouse 运行 Headless Chrome,然后通过 chrome-remote-interface 远程序调节制浏览器,使用 Page 监察和控制页面包车型地铁加载,使用 Emulation 模块调治视口缩放,最终生成一张截图。

JavaScript

const { ChromeLauncher } = require('lighthouse/lighthouse-cli/chrome-launcher') const chrome = require('chrome-remote-interface') const fs = require('fs') const deviceMetrics = { width: 1200, height: 800, deviceScaleFactor: 0, mobile: false, fitWindow: false } const screenshotMetrics = { width: deviceMetrics.width, height: deviceMetrics.height } let protocol let launcher function launchChrome () { const launcher = new ChromeLauncher({ port: 9222, autoSelectChrome: true, additionalFlags: ['--window-size=412,732', '--disable-gpu', '--headless'] }) return launcher.run().then(() => launcher) } function getScreenShot () { const { Page, Emulation } = protocol return Page.enable() .then(() => { Emulation.setDeviceMetricsOverride(deviceMetrics) // 配置浏览器尺寸 Emulation.setVisibleSize(screenshotMetrics) // 配置截图尺寸 Page.navigate({ url: '' }) return new Promise((resolve, reject) => { Page.loadEventFired(() => { resolve(Page.captureScreenshot({ format: 'jpeg', fromSurface: true })) }) }) }) .then(image => { const buffer = new Buffer(image.data, 'base64') return new Promise((resolve, reject) => { fs.writeFile('output.jpeg', buffer, 'base64', err => { if (err) return reject(err) resolve() }) }) }) } launchChrome() .then(Launcher => { launcher = Launcher return new Promise((resolve, reject) =>{ chrome(Protocol => { protocol = Protocol resolve() }).on('error', err => { reject(err) }) }) }) .then(getScreenShot) .then(() => { protocol.close() launcher.kill() }) .catch(console.error)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
const { ChromeLauncher } = require('lighthouse/lighthouse-cli/chrome-launcher')
const chrome = require('chrome-remote-interface')
const fs = require('fs')
const deviceMetrics = {
  width: 1200,
  height: 800,
  deviceScaleFactor: 0,
  mobile: false,
  fitWindow: false
}
const screenshotMetrics = {
  width: deviceMetrics.width,
  height: deviceMetrics.height
}
let protocol
let launcher
 
function launchChrome () {
  const launcher = new ChromeLauncher({
    port: 9222,
    autoSelectChrome: true,
    additionalFlags: ['--window-size=412,732', '--disable-gpu', '--headless']
  })
  return launcher.run().then(() => launcher)
}
function getScreenShot () {
  const { Page, Emulation } = protocol
  return Page.enable()
    .then(() => {
      Emulation.setDeviceMetricsOverride(deviceMetrics) // 配置浏览器尺寸
      Emulation.setVisibleSize(screenshotMetrics) // 配置截图尺寸
      Page.navigate({ url: 'https://github.com/' })
      return new Promise((resolve, reject) => {
        Page.loadEventFired(() => {
          resolve(Page.captureScreenshot({ format: 'jpeg', fromSurface: true }))
        })
      })
    })
    .then(image => {
      const buffer = new Buffer(image.data, 'base64')
      return new Promise((resolve, reject) => {
        fs.writeFile('output.jpeg', buffer, 'base64', err => {
          if (err) return reject(err)
          resolve()
        })
      })
    })
}
launchChrome()
  .then(Launcher => {
    launcher = Launcher
    return new Promise((resolve, reject) =>{
      chrome(Protocol => {
        protocol = Protocol
        resolve()
      }).on('error', err => { reject(err) })
    })
  })
  .then(getScreenShot)
  .then(() => {
    protocol.close()
    launcher.kill()
  })
  .catch(console.error)

这里运用 lighthouse 提供的 ChromeLauncher 模块来调用 Chrome,假如Computer上安装了Chrome Canary,lighthouse 暗许会运转 Chrome Canary,能够将 autoSelectChrome 设置为false 然后自动选用使用什么版本。

通过 chrome-remote-interface 协作 Headless Chrome,我们还足以做越来越多事情。

使用 CSS 和 DOM 模块,能够得到和安装页面中的 DOM 节点内容和 CSS 样式。

JavaScript

function getStyle () { const { Page, CSS, DOM } = protocol return Promise.all([ DOM.enable(), CSS.enable(), Page.enable() ]) .then(() => { Page.navigate({ url: '' }) return new Promise((resolve, _) => { Page.loadEventFired(() => { resolve(DOM.getDocument()) }) }) }) .then(res => res.root.nodeId) .then(nodeId => DOM.querySelector({ selector: '.btn-primary', nodeId })) .then(({ nodeId }) => CSS.getComputedStyleForNode({ nodeId })) .then(style => { console.log(style) }) }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function getStyle () {
  const { Page, CSS, DOM } = protocol
  return Promise.all([
      DOM.enable(),
      CSS.enable(),
      Page.enable()
    ])
    .then(() => {
      Page.navigate({ url: 'https://github.com/' })
      return new Promise((resolve, _) => {
        Page.loadEventFired(() => { resolve(DOM.getDocument()) })
      })
    })
    .then(res => res.root.nodeId)
    .then(nodeId => DOM.querySelector({ selector: '.btn-primary', nodeId }))
    .then(({ nodeId }) => CSS.getComputedStyleForNode({ nodeId }))
    .then(style => { console.log(style) })
}

运用 Runtime 模块,能够在页面运转时实行 JS 脚本。

JavaScript

function search () { const { Page, Runtime } = protocol return Promise.all([ Page.enable() ]) .then(() => { Page.navigate({ url: '' }) return new Promise((resolve, _) => { Page.loadEventFired(() => { resolve() }) }) }) .then(() => { const code = [ 'var input = document.querySelector('.s_ipt')', 'var btn = document.querySelector('#su')', 'input.value='123'' ].join(';') return Runtime.evaluate({ expression: code }) }) .then(() => { return new Promise((resolve, _) => { setTimeout(() => { resolve(Page.captureScreenshot({ format: 'jpeg', fromSurface: true })) }, 3000) }) }) .then(image => { const buffer = new Buffer(image.data, 'base64') return new Promise((resolve, reject) => { fs.writeFile('output.jpeg', buffer, 'base64', err => { if (err) return reject(err) resolve() }) }) }) }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function search () {
  const { Page, Runtime } = protocol
  return Promise.all([
      Page.enable()
    ])
    .then(() => {
      Page.navigate({ url: 'https://www.baidu.com/' })
      return new Promise((resolve, _) => {
        Page.loadEventFired(() => { resolve() })
      })
    })
    .then(() => {
      const code = [
        'var input = document.querySelector('.s_ipt')',
        'var btn = document.querySelector('#su')',
        'input.value='123''
      ].join(';')
      return Runtime.evaluate({ expression: code })
    })
    .then(() => {
      return new Promise((resolve, _) => {
        setTimeout(() => {
          resolve(Page.captureScreenshot({ format: 'jpeg', fromSurface: true }))
        }, 3000)
      })
    })
    .then(image => {
      const buffer = new Buffer(image.data, 'base64')
      return new Promise((resolve, reject) => {
        fs.writeFile('output.jpeg', buffer, 'base64', err => {
          if (err) return reject(err)
          resolve()
        })
      })
    })
}

采用 Network 模块,能够读取并安装 UserAgent 和 Cookie 等消息。

JavaScript

function setUAandCookie () { const { Page, Network } = protocol return Promise.all([ Network.enable(), Page.enable() ]) .then(() => { const userAgent = Network.setUserAgentOverride({ userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.71 Safari/537.36" }) Network.setCookie({ url: '', name: 'test', value: '123', domain: '.github.com', path: '/', httpOnly: true }) Page.navigate({ url: '' }) return new Promise((resolve, _) => { Page.loadEventFired(() => { resolve() }) }) }) .then(() => { return Network.getCookies() }) .then(console.log) }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function setUAandCookie () {
  const { Page, Network } = protocol
  return Promise.all([
      Network.enable(),
      Page.enable()
    ])
    .then(() => {
      const userAgent =
      Network.setUserAgentOverride({ userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.71 Safari/537.36" })
      Network.setCookie({
        url: 'https://github.com',
        name: 'test',
        value: '123',
        domain: '.github.com',
        path: '/',
        httpOnly: true
      })
      Page.navigate({ url: 'https://github.com/' })
      return new Promise((resolve, _) => {
        Page.loadEventFired(() => { resolve() })
      })
    })
    .then(() => {
      return Network.getCookies()
    })
    .then(console.log)
}

异步链式调用

任何重临 Promise 的函数都能够透过 .then() 链式调用。前二个 resolve 的结果会传递给后二个:

asyncDBconnect('') .then(asyncGetSession) // 传递 asyncDBconnect 的结果 .then(asyncGetUser) // 传递 asyncGetSession 的结果 .then(asyncLogAccess) // 传递 asyncGetUser 的结果 .then(result => { // 同步函数 console.log('complete'); // (传递 asyncLogAccess 的结果) return result; // (结果传给下二个 .then()) }) .catch(err => { // 任何八个 reject 触发 console.log('error', err); });

1
2
3
4
5
6
7
8
9
10
11
asyncDBconnect('http://localhost:1234')
  .then(asyncGetSession)      // 传递 asyncDBconnect 的结果
  .then(asyncGetUser)         // 传递 asyncGetSession 的结果
  .then(asyncLogAccess)       // 传递 asyncGetUser 的结果
  .then(result => {           // 同步函数
    console.log('complete');  //   (传递 asyncLogAccess 的结果)
    return result;            //   (结果传给下一个 .then())
  })
  .catch(err => {             // 任何一个 reject 触发
    console.log('error', err);
  });

协助举行函数也能够实践 .then(),再次回到的值传递给下三个 .then()(如果有)。

当其余三个面前的 reject 触发时,.catch() 函数会被调用。触发 reject 的函数前面包车型地铁 .then() 也不再试行。贯穿整个链条能够存在两个 .catch() 方法,进而捕获分裂的荒谬。

ES2018 引入了 .finally() 方法,它不管重回结果什么,都会试行最后逻辑 – 比方,清理操作,关闭数据库连接等等。当前只有 Chrome 和 Firefox 扶持,不过 TC39 技委已经宣布了 .finally() 补丁。

function doSomething() { doSomething1() .then(doSomething2) .then(doSomething3) .catch(err => { console.log(err); }) .finally(() => { // 清理操作放那儿! }); }

1
2
3
4
5
6
7
8
9
10
11
function doSomething() {
  doSomething1()
  .then(doSomething2)
  .then(doSomething3)
  .catch(err => {
    console.log(err);
  })
  .finally(() => {
    // 清理操作放这儿!
  });
}

金沙棋牌官方平台 1前言

近期机关在招前端,作为机关唯大器晚成的前端,面试了过多应聘的同班,面试中有四个关系 Promise 的二个标题是:

网页中预加载20张图片资源,分步加载,一回加载10张,两回到位,怎么决定图片须求的面世,怎么样感知当前异步诉求是或不是已做到?

只是能豆蔻梢头体答上的非常少,可以交给二个回调 + 计数版本的,作者都是为合格了。那么接下去就一起来上学总括一下基于 Promise 来管理异步的二种方法。

本文的例子是一个极端简化的三个卡通阅读器,用4张漫画图的加载来介绍异步管理不一致方法的贯彻和反差,以下是 HTML 代码:

JavaScript

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Promise</title> <style> .pics{ width: 300px; margin: 0 auto; } .pics img{ display: block; width: 100%; } .loading{ text-align: center; font-size: 14px; color: #111; } </style> </head> <body> <div class="wrap"> <div class="loading">正在加载...</div> <div class="pics"> </div> </div> <script> </script> </body> </html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Promise</title>
  <style>
    .pics{
      width: 300px;
      margin: 0 auto;
    }
    .pics img{
      display: block;
      width: 100%;
    }
    .loading{
      text-align: center;
      font-size: 14px;
      color: #111;
    }
  </style>
</head>
<body>
  <div class="wrap">
    <div class="loading">正在加载...</div>
    <div class="pics">
    </div>
  </div>
  <script>
  </script>
</body>
</html>

throw new Error('error!!!')

初探 Headless Chrome

2017/06/15 · 根基手艺 · Chrome

原稿出处: 饿了么前端   

Promises, Promises

async / await 如故依赖 Promise 对象,最后依据回调。你必要驾驭Promise 的劳作规律,它也并不一致等 Promise.all()Promise.race()。比较容易忽视的是 Promise.all(),这么些命令比使用豆蔻年华多元非亲非故的 await 命令更便捷。

题外话

我们最近有 1 个前端的 HC,base 日内瓦,一家具备 50 架飞机的物流公司的AI部门,须求专业经验八年以上,那是商城社招须要的。 感兴趣的就关系自己吧,Email: d2hlYX奇骏vQGZveG1haWwuY29t

  console.log(3)

在 Karma 中利用 Headless Chrome 实行单元测量试验

相比较之下于 PhantomJS 等,使用 Headless Chrome 做单元测量试验特别附近浏览器开采条件。同有的时候候 PhantomJS 小编也少年老成度功成身退,在 Chrome 公布 Headless 格局后,发表通知不再维护 PhantomJS 项目。

金沙棋牌官方平台,设置信赖

JavaScript

npm install jasmine-core karma karma-chrome-launcher karma-jasmine -D

1
npm install jasmine-core karma karma-chrome-launcher karma-jasmine -D

配置 Karma

JavaScript

// karma.conf.js module.exports = function (config) { config.set({ frameworks: ['jasmine'], files: ['./test.js'], browsers: ["Chrome_Beta_Headless"], customLaunchers: { Chrome_Beta_Headless: { base: 'Chrome', flags: [ '--headless', '--disable-gpu', '--remote-debugging-port=9222' ] } }, browserConsoleLogOptions: { level: 'log', terminal: true }, reporters: ['progress'], autoWatch: false, singleRun: true }) }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// karma.conf.js
module.exports = function (config) {
  config.set({
    frameworks: ['jasmine'],
    files: ['./test.js'],
    browsers: ["Chrome_Beta_Headless"],
    customLaunchers: {
      Chrome_Beta_Headless: {
        base: 'Chrome',
        flags: [
          '--headless',
          '--disable-gpu',
          '--remote-debugging-port=9222'
        ]
      }
    },
    browserConsoleLogOptions: {
      level: 'log',
      terminal: true
    },
    reporters: ['progress'],
    autoWatch: false,
    singleRun: true
  })
}

编写测量试验用例

JavaScript

// test.js describe('test', function() { it('should be true', function() { console.log(window.navigator.userAgent) expect(true).toEqual(true); }); });

1
2
3
4
5
6
7
// test.js
describe('test', function() {
  it('should be true', function() {
    console.log(window.navigator.userAgent)
    expect(true).toEqual(true);
  });
});

配置npm script

JavaScript

// package.json ... scripts: { test: "karma start" } ...

1
2
3
4
5
6
// package.json
...
scripts: {
  test: "karma start"
}
...

在尖峰中运作

JavaScript

npm test

1
npm test

结果金沙棋牌官方平台 2

从再次回到结果中得以看见,测量检验已运营在 Headless Chrome 境况下。

Async/Await

Promise 看起来有个别复杂,所以 ES2017 引进了 asyncawait。即便只是语法糖,却使 Promise 尤其有辅助,并且能够免止 .then() 链式调用的主题素材。看上面选拔 Promise 的例子:

function connect() { return new Promise((resolve, reject) => { asyncDBconnect('') .then(asyncGetSession) .then(asyncGetUser) .then(asyncLogAccess) .then(result => resolve(result)) .catch(err => reject(err)) }); } // 运营 connect 方法 (自实践办法) (() => { connect(); .then(result => console.log(result)) .catch(err => console.log(err)) })();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function connect() {
 
  return new Promise((resolve, reject) => {
 
    asyncDBconnect('http://localhost:1234')
      .then(asyncGetSession)
      .then(asyncGetUser)
      .then(asyncLogAccess)
      .then(result => resolve(result))
      .catch(err => reject(err))
 
  });
}
 
// 运行 connect 方法 (自执行方法)
(() => {
  connect();
    .then(result => console.log(result))
    .catch(err => console.log(err))
})();

使用 async / await 重写上边的代码:

  1. 外表方法用 async 声明
  2. 依据 Promise 的异步方法用 await 阐明,能够确定保证下叁个发令实施前,它已试行到位

async function connect() { try { const connection = await asyncDBconnect(''), session = await asyncGetSession(connection), user = await asyncGetUser(session), log = await asyncLogAccess(user); return log; } catch (e) { console.log('error', err); return null; } } // 运维 connect 方法 (自进行异步函数) (async () => { await connect(); })();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
async function connect() {
 
  try {
    const
      connection = await asyncDBconnect('http://localhost:1234'),
      session = await asyncGetSession(connection),
      user = await asyncGetUser(session),
      log = await asyncLogAccess(user);
 
    return log;
  }
  catch (e) {
    console.log('error', err);
    return null;
  }
 
}
 
// 运行 connect 方法 (自执行异步函数)
(async () => { await connect(); })();

await 使每一种异步调用看起来像是同步的,同有的时候候不贻误 JavaScript 的单线程管理。别的,async 函数总是回到三个 Promise 对象,由此它能够被别的 async 函数调用。

async / await 大概不会让代码减少,但是有过多优点:

  1. 语法更清晰。括号越来越少,出错的恐怕性也尤为小。
  2. 调度更便于。能够在任何 await 表明处设置断点。
  3. 错误管理尚佳。try / catch 能够与同盟代码应用同样的管理情势。
  4. 支撑美好。全部浏览器(除了 IE 和 Opera Mini )和 Node7.6+ 均已完成。

如是说,未有两全的…

并发恳求,按顺序管理结果

Promise.all 尽管能并发五个恳求,然则风姿浪漫旦中间某三个 promise 出错,整个 promise 会被 reject 。 webapp 里常用的能源预加载,也许加载的是 20 张逐帧图片,当互连网现身难点, 20 张图难免会有风华正茂两张号令失利,如若退步后,直接遗弃别的被 resolve 的回到结果,就如有一些不妥,大家只要了然什么图片出错了,把失误的图片再做二回呼吁或着用占位图补上就好。 上节中的代码 const promises = urls.map(loadImg) 运营后,全体都图片乞求都曾经发出去了,大家如若按顺序依次管理 promises 这些数组中的 Promise 实例就好了,先用三个总结点的 for 循环来贯彻以下,跟首节中的单风姿洒脱央求相像,利用 Promise 链来挨门挨户管理。

JavaScript

let task = Promise.resolve() for (let i = 0; i < promises.length; i++) { task = task.then(() => promises[i]).then(addToHtml) }

1
2
3
4
let task = Promise.resolve()
for (let i = 0; i < promises.length; i++) {
  task = task.then(() => promises[i]).then(addToHtml)
}

改成 reduce 版本

JavaScript

promises.reduce((task, imgPromise) => { return task.then(() => imgPromise).then(addToHtml) }, Promise.resolve())

1
2
3
promises.reduce((task, imgPromise) => {
  return task.then(() => imgPromise).then(addToHtml)
}, Promise.resolve())

demo3地址:Promise 并发诉求,顺序管理结果

const promise = new Promise((resolve, reject) => {

小结

本文简介了眨眼之间间 Headless Chrome 在极端的用法,以至如何使用 Headless Chrome 获取截图、获取页面中的CSS和DOM、设置UA和库克ie、运转JS脚本、同盟Karma 进行单元测验。接下来,就等着您索求越来越多关于 Headless Chrome 的用法了…

参考:

https://developers.google.com/web/updates/2017/04/headless-chrome
How to install and use Headless Chrome on OSX

1 赞 1 收藏 评论

金沙棋牌官方平台 3

应用 Promise.all() 管理几个异步操作

Promise .then() 方法用于各种实施的异步函数。假诺不关切顺序 – 举个例子,伊始化不相干的零器件 – 全数异步函数同有时候起步,直到最慢的函数试行 resolve,整个工艺流程甘休。

Promise.all() 适用于这种气象,它接收二个函数数组并且再次来到另一个Promise。譬如:

Promise.all([ async1, async2, async3 ]) .then(values => { // 重临值的数组 console.log(values); // (与函数数组顺序生机勃勃致) return values; }) .catch(err => { // 任风流浪漫 reject 被触发 console.log('error', err); });

1
2
3
4
5
6
7
8
Promise.all([ async1, async2, async3 ])
  .then(values => {           // 返回值的数组
    console.log(values);      // (与函数数组顺序一致)
    return values;
  })
  .catch(err => {             // 任一 reject 被触发
    console.log('error', err);
  });

私自一个异步函数 rejectPromise.all() 会马上终止。

支配最大并发数

方今大家来试着成功一下地点的笔试题,那个其实都__不必要调整最大并发数__。 20张图,分一遍加载,那用四个 Promise.all 不就一下子就解决了了?不过用 Promise.all不能侦听到每一张图片加载成功的事件。而用上风度翩翩节的措施,大家既可以并发须要,又能按顺序响应图片加载成功的事件。

JavaScript

let index = 0 const step1 = [], step2 = [] while(index < 10) { step1.push(loadImg(`./images/pic/${index}.jpg`)) index += 1 } step1.reduce((task, imgPromise, i) => { return task .then(() => imgPromise) .then(() => { console.log(`第 ${i + 1} 张图片加载完结.`) }) }, Promise.resolve()) .then(() => { console.log('>> 前边10张已经加载完!') }) .then(() => { while(index < 20) { step2.push(loadImg(`./images/pic/${index}.jpg`)) index += 1 } return step2.reduce((task, imgPromise, i) => { return task .then(() => imgPromise) .then(() => { console.log(`第 ${i + 11} 张图片加载实现.`) }) }, Promise.resolve()) }) .then(() => { console.log('>> 后面10张已经加载完') })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
let index = 0
const step1 = [], step2 = []
 
while(index < 10) {
  step1.push(loadImg(`./images/pic/${index}.jpg`))
  index += 1
}
 
step1.reduce((task, imgPromise, i) => {
  return task
    .then(() => imgPromise)
    .then(() => {
      console.log(`第 ${i + 1} 张图片加载完成.`)
    })
}, Promise.resolve())
  .then(() => {
    console.log('>> 前面10张已经加载完!')
  })
  .then(() => {
    while(index < 20) {
      step2.push(loadImg(`./images/pic/${index}.jpg`))
      index += 1
    }
    return step2.reduce((task, imgPromise, i) => {
      return task
        .then(() => imgPromise)
        .then(() => {
          console.log(`第 ${i + 11} 张图片加载完成.`)
        })
    }, Promise.resolve())
  })
  .then(() => {
    console.log('>> 后面10张已经加载完')
  })

地方的代码是针对难点的 hardcode ,假若笔试的时候能写出这些,都早已然是这些科学了,可是并未一个人写出来,said…

demo4地址(看调整台和互联网央求):Promise 分步加载 – 1

那正是说大家在空虚一下代码,写多个通用的办法出来,那么些函数再次来到贰个Promise,还足以持续管理整个都图片加载完后的异步回调。

JavaScript

function stepLoad (urls, handler, stepNum) { const createPromises = function (now, stepNum) { let last = Math.min(stepNum + now, urls.length) return urls.slice(now, last).map(handler) } let step = Promise.resolve() for (let i = 0; i < urls.length; i += stepNum) { step = step .then(() => { let promises = createPromises(i, stepNum) return promises.reduce((task, imgPromise, index) => { return task .then(() => imgPromise) .then(() => { console.log(`第 ${index + 1

  • i} 张图片加载达成.`) }) }, Promise.resolve()) }) .then(() => { let current = Math.min(i + stepNum, urls.length) console.log(`>> 总共${current}张已经加载完!`) }) } return step }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function stepLoad (urls, handler, stepNum) {
const createPromises = function (now, stepNum) {
    let last = Math.min(stepNum + now, urls.length)
    return urls.slice(now, last).map(handler)
  }
  let step = Promise.resolve()
  for (let i = 0; i < urls.length; i += stepNum) {
    step = step
      .then(() => {
        let promises = createPromises(i, stepNum)
        return promises.reduce((task, imgPromise, index) => {
          return task
            .then(() => imgPromise)
            .then(() => {
              console.log(`第 ${index + 1 + i} 张图片加载完成.`)
            })
        }, Promise.resolve())
      })
      .then(() => {
        let current = Math.min(i + stepNum, urls.length)
        console.log(`>> 总共${current}张已经加载完!`)
      })
  }
return step
}

地方代码里的 for 也得以改成 reduce ,但是须要先将急需加载的 urls 按分步的数据,划分成数组,感兴趣的敌人能够友善写写看。

demo5地址(看调节台和网络乞求):Promise 分步 – 2

但地点的达成和大家说的__最大并发数调整__不要紧关联啊,最大并发数调整是指:当加载 20 张图片加载的时候,先并发央浼 10 张图纸,当一张图纸加载成功后,又会继续倡导一张图片的号令,让并发数保持在 12个,直到须要加载的图片都全部倡导倡议。这些在写爬虫中得以说是相比遍布的使用处境了。 那么我们分局方的有个别学问,大家用二种艺术来贯彻这几个意义。

    at startup (bootstrap_node.js:187:16)

哪些在终极中采纳

在Mac上采取前,提出先绑定 Chrome 的别称

JavaScript

alias google-chrome="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"

1
alias google-chrome="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"

Linux下没有必要绑定别名,从官方网址络下载最新版 Chrome 之后平素运转以下命令就能够。

下一场,在顶峰中输入:

google-chrome --headless --disable-gpu --remote-debugging-port=9222

1
google-chrome --headless --disable-gpu --remote-debugging-port=9222  https://github.com

充实 –disable-gpu 首若是为了挡住现阶段只怕接触的谬误。

那个时候,Headless Chrome已经打响运转了。展开浏览器,输入 http://localhost:9222,你会见到如下的分界面:金沙棋牌官方平台 4

在尖峰中,大家还是可以够做以下操作:

获取显示器截图:

JavaScript

google-chrome --headless --disable-gpu --screenshot --window-size=1280,1696

1
google-chrome --headless --disable-gpu --screenshot --window-size=1280,1696 https://github.com

取得页面为PDF:

JavaScript

google-chrome --headless --disable-gpu --print-to-pdf

1
google-chrome --headless --disable-gpu --print-to-pdf https://github.com

打字与印刷页面DOM:

JavaScript

google-chrome --headless --disable-gpu --dump-dom

1
google-chrome --headless --disable-gpu --dump-dom https://github.com/

JavaScript 之旅

异步编制程序是 JavaScript 相当小概幸免的挑衅。回调在大部应用中是必须的,不过轻易陷于深度嵌套的函数中。

Promise 抽象了回调,然则有为数不菲句法陷阱。转变本来就有函数恐怕是风流洒脱件苦差事,·then() 链式调用看起来很糊涂。

很幸运,async/await 表达清晰。代码看起来是一块的,但是又不独自据有单个管理线程。它将转移你书写 JavaScript 的格局,以致让您更讲求 Promise – 假设没接触过的话。

1 赞 收藏 评论

金沙棋牌官方平台 5

参考资料

  • JavaScript Promise:简介 | Web | Google Developers
  • JavaScript Promise迷你书(中文版)

    1 赞 3 收藏 评论

金沙棋牌官方平台 6

promise1 Promise { <pending> }

联手循环中的异步等待

少数意况下,你想要在一同循环中调用异步函数。举例:

async function process(array) { for (let i of array) { await doSomething(i); } }

1
2
3
4
5
async function process(array) {
  for (let i of array) {
    await doSomething(i);
  }
}

不起成效,上边包车型大巴代码也同等:

async function process(array) { array.forEach(async i => { await doSomething(i); }); }

1
2
3
4
5
async function process(array) {
  array.forEach(async i => {
    await doSomething(i);
  });
}

巡回本身保持同步,况兼总是在此中异步操作早前实现。

ES2018 引进异步迭代器,除了 next() 方法重临多个 Promise 对象之外,与健康迭代器相符。由此,await 关键字能够与 for ... of 循环一齐行使,以串行情势运转异步操作。举例:

async function process(array) { for await (let i of array) { doSomething(i); } }

1
2
3
4
5
async function process(array) {
  for await (let i of array) {
    doSomething(i);
  }
}

只是,在异步迭代器实现从前,最佳的方案是将数组每项 mapasync 函数,并用 Promise.all() 实践它们。举例:

const todo = ['a', 'b', 'c'], alltodo = todo.map(async (v, i) => { console.log('iteration', i); await processSomething(v); }); await Promise.all(alltodo);

1
2
3
4
5
6
7
8
const
  todo = ['a', 'b', 'c'],
  alltodo = todo.map(async (v, i) => {
    console.log('iteration', i);
    await processSomething(v);
});
 
await Promise.all(alltodo);

那样方便推行并行职务,可是力所不及将二回迭代结果传递给另一回迭代,并且映射大数组也许会损耗计算品质。

使用 Promise.race

Promise.race 选拔二个 Promise 数组,重返这一个数组中最初被 resolve 的 Promise 的再次来到值。终于找到 Promise.race 的应用意况了,先来利用那一个办法完成的功用代码:

JavaScript

function limitLoad (urls, handler, limit) { const sequence = [].concat(urls) // 对数组做八个拷贝 let count = 0 let promises const wrapHandler = function (url) { const promise = handler(url).then(img => { return { img, index: promise } }) return promise } //并发央浼到最大数 promises = sequence.splice(0, limit).map(url => { return wrapHandler(url) }) // limit 大于一切图纸数, 并发全体伸手 if (sequence.length <= 0) { return Promise.all(promises) } return sequence.reduce((last, url) => { return last.then(() => { return Promise.race(promises) }).catch(err => { console.error(err) }).then((res) => { let pos = promises.findIndex(item => { return item == res.index }) promises.splice(pos, 1) promises.push(wrapHandler(url)) }) }, Promise.resolve()).then(() => { return Promise.all(promises) }) }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function limitLoad (urls, handler, limit) {
  const sequence = [].concat(urls) // 对数组做一个拷贝
  let count = 0
  let promises
  const wrapHandler = function (url) {
    const promise = handler(url).then(img => {
      return { img, index: promise }
    })
    return promise
  }
  //并发请求到最大数
  promises = sequence.splice(0, limit).map(url => {
    return wrapHandler(url)
  })
  // limit 大于全部图片数, 并发全部请求
  if (sequence.length <= 0) {
    return Promise.all(promises)
  }
  return sequence.reduce((last, url) => {
    return last.then(() => {
      return Promise.race(promises)
    }).catch(err => {
      console.error(err)
    }).then((res) => {
      let pos = promises.findIndex(item => {
        return item == res.index
      })
      promises.splice(pos, 1)
      promises.push(wrapHandler(url))
    })
  }, Promise.resolve()).then(() => {
    return Promise.all(promises)
  })
}

设定最大乞请数为 5,Chrome 中呼吁加载的 timeline :金沙棋牌官方平台 7

demo7地址(看调控台和网络央求):Promise 调控最大并发数 – 方法2

在运用 Promise.race 完毕这些效果,首若是无休止的调用 Promise.race 来回到已经被 resolve 的职分,然后从 promises 中删掉那几个 Promise 对象,再投入二个新的 Promise,直到整个的 U福睿斯L 被取完,最终再使用 Promise.all 来管理全数图片完结后的回调。

  resolve()

Promises

ES2015(ES6) 引入了 Promises。回调函数仍有用,但是Promises 提供了更显明的链式异步命令语法,由此得以串联运营(下个章节会讲)。

筹算依照 Promise 封装,异步回调函数必得重临一个 Promise 对象。Promise 对象会实践以下四个函数(作为参数字传送递的卡塔尔此中之生机勃勃:

  • resolve:施行成功回调
  • reject:推行倒闭回调

以下例子,database API 提供了一个 connect() 方法,选用一个回调函数。外界的 asyncDBconnect() 函数立时重回了二个新的 Promise,风华正茂旦延续成立成功或战败,resolve()reject() 便会举办:

const db = require('database'); // 连接数据库 function asyncDBconnect(param) { return new Promise((resolve, reject) => { db.connect(param, (err, connection) => { if (err) reject(err); else resolve(connection); }); }); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const db = require('database');
 
// 连接数据库
function asyncDBconnect(param) {
 
  return new Promise((resolve, reject) => {
 
    db.connect(param, (err, connection) => {
      if (err) reject(err);
      else resolve(connection);
    });
 
  });
 
}

Node.js 8.0 以上提供了 util.promisify() 功能,能够把根据回调的函数调换来基于 Promise 的。有三个应用法规:

  1. 流传一个唯风度翩翩的异步函数
  2. 流传的函数希望是不当优先的(比如:(err, value) => …卡塔尔,error 参数在前,value 随后

举例:

// Node.js: 把 fs.readFile promise 化 const util = require('util'), fs = require('fs'), readFileAsync = util.promisify(fs.readFile); readFileAsync('file.txt');

1
2
3
4
5
6
7
// Node.js: 把 fs.readFile promise 化
const
  util = require('util'),
  fs = require('fs'),
  readFileAsync = util.promisify(fs.readFile);
 
readFileAsync('file.txt');

各类库都会提供本身的 promisify 方法,寥寥几行也得以自个儿撸二个:

// promisify 只接到叁个函数参数 // 传入的函数采取 (err, data) 参数 function promisify(fn) { return function() { return new Promise( (resolve, reject) => fn( ...Array.from(arguments), (err, data) => err ? reject(err) : resolve(data) ) ); } } // 比方 function wait(time, callback) { setTimeout(() => { callback(null, 'done'); }, time); } const asyncWait = promisify(wait); ayscWait(1000);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// promisify 只接收一个函数参数
// 传入的函数接收 (err, data) 参数
function promisify(fn) {
  return function() {
      return new Promise(
        (resolve, reject) => fn(
          ...Array.from(arguments),
        (err, data) => err ? reject(err) : resolve(data)
      )
    );
  }
}
 
// 举例
function wait(time, callback) {
  setTimeout(() => { callback(null, 'done'); }, time);
}
 
const asyncWait = promisify(wait);
 
ayscWait(1000);

纯净央求

最简便的,正是将异步二个个来拍卖,转为三个像样同步的情势来管理。 先来总结的贯彻多个单个 Image 来加载的 thenable 函数和叁个管理函数重临结果的函数。

JavaScript

function loadImg (url) { return new Promise((resolve, reject) => { const img = new Image() img.onload = function () { resolve(img) } img.onerror = reject img.src = url }) }

1
2
3
4
5
6
7
8
9
10
function loadImg (url) {
  return new Promise((resolve, reject) => {
    const img = new Image()
    img.onload = function () {
      resolve(img)
    }
    img.onerror = reject
    img.src = url
  })
}

异步转同步的消除观念是:当第二个 loadImg(urls[1]) 完毕后再调用 loadImg(urls[2]),依次往下。假诺 loadImg() 是三个联手函数,那么很当然的想到用__循环__。

JavaScript

for (let i = 0; i < urls.length; i++) { loadImg(urls[i]) }

1
2
3
for (let i = 0; i < urls.length; i++) {
  loadImg(urls[i])
}

当 loadImg() 为异步时,我们就只能用 Promise chain 来贯彻,最终产生这种格局的调用:

JavaScript

loadImg(urls[0]) .then(addToHtml) .then(()=>loadImg(urls[1])) .then(addToHtml) //... .then(()=>loadImg(urls[3])) .then(addToHtml)

1
2
3
4
5
6
7
loadImg(urls[0])
  .then(addToHtml)
  .then(()=>loadImg(urls[1]))
  .then(addToHtml)
  //...
  .then(()=>loadImg(urls[3]))
  .then(addToHtml)

那大家用二个个中变量来存款和储蓄当前的 promise ,就如链表的游标相通,修正后的 for 循环代码如下:

JavaScript

let promise = Promise.resolve() for (let i = 0; i < urls.length; i++) { promise = promise .then(()=>loadImg(urls[i])) .then(addToHtml) }

1
2
3
4
5
6
let promise = Promise.resolve()
for (let i = 0; i < urls.length; i++) {
promise = promise
.then(()=>loadImg(urls[i]))
.then(addToHtml)
}

promise 变量就好像二个迭代器,不断指向最新的回到的 Promise,那大家就更是应用 reduce 来简化代码。

JavaScript

urls.reduce((promise, url) => { return promise .then(()=>loadImg(url)) .then(addToHtml) }, Promise.resolve())

1
2
3
4
5
urls.reduce((promise, url) => {
  return promise
    .then(()=>loadImg(url))
    .then(addToHtml)
}, Promise.resolve())

在前后相继设计中,是能够通过函数的__递归__来兑现循环语句的。所以大家将上边的代码改成__递归__:

JavaScript

function syncLoad (index) { if (index >= urls.length) return loadImg(urls[index]).then(img => { // process img addToHtml(img) syncLoad (index + 1) }) } // 调用 syncLoad(0)

1
2
3
4
5
6
7
8
9
10
11
function syncLoad (index) {
  if (index >= urls.length) return
      loadImg(urls[index]).then(img => {
      // process img
      addToHtml(img)
      syncLoad (index + 1)
    })
}
 
// 调用
syncLoad(0)

好了一个简易的异步转同步的兑现方式就早就成功,大家来测验一下。 那一个完成的粗略版本已经落成没难点,但是最上面的正在加载还在,那大家怎么在函数外部知道那么些递归的甘休,并隐瞒掉那么些DOM 呢?Promise.then() 形似重回的是 thenable 函数 大家只必要在 syncLoad 内部传递那条 Promise 链,直到最终的函数重回。

JavaScript

function syncLoad (index) { if (index >= urls.length) return Promise.resolve() return loadImg(urls[index]) .then(img => { addToHtml(img) return syncLoad (index + 1) }) } // 调用 syncLoad(0) .then(() => { document.querySelector('.loading').style.display = 'none' })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function syncLoad (index) {
  if (index >= urls.length) return Promise.resolve()
  return loadImg(urls[index])
    .then(img => {
      addToHtml(img)
      return syncLoad (index + 1)
    })
}
 
// 调用
syncLoad(0)
  .then(() => {
  document.querySelector('.loading').style.display = 'none'
})

后天大家再来康健一下以此函数,让它越是通用,它承当__异步函数__、异步函数必要的参数数组、__异步函数的回调函数__七个参数。而且会记录调用退步的参数,在最后回来到函数外界。别的大家能够考虑一下为何catch 要在结尾的 then 以前。

JavaScript

function syncLoad (fn, arr, handler) { if (typeof fn !== 'function') throw TypeError('第一个参数必需是function') if (!Array.isArray(arr)) throw TypeError('首个参数必需是数组') handler = typeof fn === 'function' ? handler : function () {} const errors = [] return load(0) function load (index) { if (index >= arr.length) { return errors.length > 0 ? Promise.reject(errors) : Promise.resolve() } return fn(arr[index]) .then(data => { handler(data) }) .catch(err => { console.log(err) errors.push(arr[index]) return load(index + 1) }) .then(() => { return load (index + 1) }) } } // 调用 syncLoad(loadImg, urls, addToHtml) .then(() => { document.querySelector('.loading').style.display = 'none' }) .catch(console.log)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function syncLoad (fn, arr, handler) {
  if (typeof fn !== 'function') throw TypeError('第一个参数必须是function')
  if (!Array.isArray(arr)) throw TypeError('第二个参数必须是数组')
  handler = typeof fn === 'function' ? handler : function () {}
  const errors = []
  return load(0)
  function load (index) {
    if (index >= arr.length) {
      return errors.length > 0 ? Promise.reject(errors) : Promise.resolve()
    }
    return fn(arr[index])
      .then(data => {
        handler(data)
      })
      .catch(err => {
        console.log(err)              
        errors.push(arr[index])
        return load(index + 1)
      })
      .then(() => {
        return load (index + 1)
      })
  }
}
 
// 调用
syncLoad(loadImg, urls, addToHtml)
  .then(() => {
    document.querySelector('.loading').style.display = 'none'
  })
  .catch(console.log)

demo1地址:单纯性诉求 – 三个 Promise 同步化

距今截至,那一个函数照旧有挺多不通用的难题,比方:管理函数必须意气风发致,不能够是种种不相同的异步函数组成的体系,异步的回调函数也只能是风姿浪漫种等。关于这种措施的更详实的陈说能够看作者事先写的黄金时代篇文章 Koa援引库之Koa-compose。

理当如此这种异步转同步的章程在此二个例子中并不是最棒的解法,但当有相符的业务场景的时候,那是很分布的解决方案。

console.log('promise1', promise1)

经过回调完结异步

单线程产生了二个题目。当 JavaScript 试行叁个“缓慢”的处理程序,举个例子浏览器中的 Ajax 央求只怕服务器上的数据库操作时,会生出怎么着?那几个操作恐怕要求几分钟 – 依然几分钟。浏览器在等待响适时会被锁定。在服务器上,Node.js 应用将不能够管理别的的顾客供给。

减轻方案是异步管理。当结果就绪时,一个进程被报告调用另贰个函数,并不是伺机完毕。那称之为回调,它看成参数字传送递给此外异步函数。举例:

doSomethingAsync(callback1); console.log('finished'); // 当 doSomethingAsync 完毕时调用 function callback1(error) { if (!error) console.log('doSomethingAsync complete'); }

1
2
3
4
5
6
7
doSomethingAsync(callback1);
console.log('finished');
 
// 当 doSomethingAsync 完成时调用
function callback1(error) {
  if (!error) console.log('doSomethingAsync complete');
}

doSomethingAsync() 采纳回调函数作为参数(只传递该函数的援引,因而支付一点都不大)。doSomethingAsync() 实践多久并不主要;我们所知道的是,callback1() 就要今后有个别时刻实践。调整台将显得:

finished doSomethingAsync complete

1
2
finished
doSomethingAsync complete

选用递归

设若大家的最大并发数是 4 ,这种办法的首要思考是一定于 4 个__单豆蔻年华伏乞__的 Promise 异步职分在同不寻常间运转运维,4 个十足伏乞不断递归取图片 UMuranoL 数组中的 U君越L 发起呼吁,直到 UEscortL 全体取完,最终再利用 Promise.all 来拍卖最后还在伸手中的异步职务,大家复用第1节__递归__本子的思路来兑现这几个功用:

JavaScript

function limitLoad (urls, handler, limit) { const sequence = [].concat(urls) // 对数组做三个拷贝 let count = 0 const promises = [] const load = function () { if (sequence.length <= 0 || count > limit) return count += 1 console.log(`近日并发数: ${count}`) return handler(sequence.shift()) .catch(err => { console.error(err) }) .then(() => { count -= 1 console.log(`当前并发数:${count}`) }) .then(() => load()) } for(let i = 0; i < limit && i < sequence.length; i++){ promises.push(load()) } return Promise.all(promises) }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function limitLoad (urls, handler, limit) {
  const sequence = [].concat(urls) // 对数组做一个拷贝
  let count = 0
  const promises = []
 
  const load = function () {
    if (sequence.length <= 0 || count > limit) return
    count += 1
    console.log(`当前并发数: ${count}`)
    return handler(sequence.shift())
      .catch(err => {
        console.error(err)
      })
      .then(() => {
        count -= 1
        console.log(`当前并发数:${count}`)
      })
      .then(() => load())
  }
 
  for(let i = 0; i < limit && i < sequence.length; i++){
    promises.push(load())
  }
  return Promise.all(promises)
}

设定最大诉求数为 5,Chrome 中号召加载的 timeline :金沙棋牌官方平台 8

demo6地址(看调节台和网络诉求):Promise 调控最大并发数 – 方法1

const promise2 = promise1.then(() => {

现代 JS 流程序调控制:从回调函数到 Promises 再到 Async/Await

2018/09/03 · JavaScript · Promises

原版的书文出处: Craig Buckler   译文出处:OFED   

JavaScript 平日被认为是异步的。那意味什么样?对开拓有怎么着震慑吗?近来,它又生出了哪些的生成?

拜会以下代码:

result1 = doSomething1(); result2 = doSomething2(result1);

1
2
result1 = doSomething1();
result2 = doSomething2(result1);

大好些个编制程序语言同步进行每行代码。第风度翩翩行实施达成再次回到一个结出。无论第风度翩翩行代码实施多久,只有进行到位第二行代码才会实践。

写在结尾

因为做事其间多量行使 ES6 的语法,Koa 中的 await/async 又是 Promise 的语法糖,所以理解 Promise 各样流程序调控制是对自己的话是特别主要的。写的有不驾驭的地点和有不当之处应接我们留言指正,别的还只怕有任何没有涉及到的主意也请我们提供一下新的秘籍和艺术。

讲明:promise 的 .then 或然 .catch 能够被调用数十次,但此处 Promise 构造函数只举办叁次。只怕说 promise 内部原因大器晚成经改换,并且有了三个值,那么继续每一次调用 .then 可能 .catch 都会直接获得该值。

回调鬼世界

平日,回调只由一个异步函数调用。因而,可以行使简单、佚名的内联函数:

doSomethingAsync(error => { if (!error) console.log('doSomethingAsync complete'); });

1
2
3
doSomethingAsync(error => {
  if (!error) console.log('doSomethingAsync complete');
});

生机勃勃多级的七个或越来越多异步调用可以经过嵌套回调函数来连接成功。举个例子:

async1((err, res) => { if (!err) async2(res, (err, res) => { if (!err) async3(res, (err, res) => { console.log('async1, async2, async3 complete.'); }); }); });

1
2
3
4
5
6
7
async1((err, res) => {
  if (!err) async2(res, (err, res) => {
    if (!err) async3(res, (err, res) => {
      console.log('async1, async2, async3 complete.');
    });
  });
});

噩运的是,这引进了回调地狱 —— 二个臭名远扬的概念,以至有专程的网页介绍!代码很难读,何况在增添错误管理逻辑时变得更糟。

回调鬼世界在客商端编码中相对少见。如若您调用 Ajax 央浼、更新 DOM 并等待动漫达成,可能须要嵌套两到三层,不过平常还算可拘押。

操作系统或服务器进度的景色就区别了。二个 Node.js API 能够收起文件上传,更新四个数据库表,写入日志,并在发送响应在此之前开展下一步的 API 调用。

promise.then((res) => {

应用 Promise.race() 管理五个异步操作

Promise.race()Promise.all() 非常近似,差别的地方在于,当首个Promise resolve 也许 reject 时,它将会 resolve 也许reject。唯有最快的异步函数会被实施:

Promise.race([ async1, async2, async3 ]) .then(value => { // 单一值 console.log(value); return value; }) .catch(err => { // 任一 reject 被触发 console.log('error', err); });

1
2
3
4
5
6
7
8
Promise.race([ async1, async2, async3 ])
  .then(value => {            // 单一值
    console.log(value);
    return value;
  })
  .catch(err => {             // 任一 reject 被触发
    console.log('error', err);
  });

Promise.resolve(1)

远景光明呢?

Promise 减弱了回调鬼世界,不过引进了别样的主题材料。

学科日常不提,整个 Promise 链条是异步的,后生可畏多种的 Promise 函数都得回来本人的 Promise 可能在终极的 .then().catch() 或者 .finally() 方法里面实施回调。

自家也断定:Promise 苦闷了本身比较久。语法看起来比回调要复杂,比较多地点会出错,调节和测量试验也成难点。可是,学习底子照旧超重大滴。

延长阅读:

  • MDN Promise documentation
  • JavaScript Promises: an Introduction
  • JavaScript Promises … In Wicked Detail
  • Promises for asynchronous programming

1

  .then((res) => {

})

    at Function.Module.runMain (module.js:667:11)

  console.log('promise2', promise2)

success 1007

promise1 Promise { 'success' }

    return 3

解释:.then 或 .catch 再次回到的值不能够是 promise 自己,不然会招致死循环。相像于:

题目五

promise.catch(console.error)

promise.then((res) => {

2

  setTimeout(() => {

  resolve('success1')

【嵌牛正文】:以下 promise 均代表 Promise 实例,意况是 Node.js。

题目十

题目七

  .then((res) => {

分解:.then 只怕 .catch 的参数期待是函数,传入非函数则会发出值穿透。

    console.error('fail1: ', e)

分解:process.nextTick 和 promise.then 都归于 microtask,而 setImmediate 归属 macrotask,在事变循环的 check 阶段执行。事件循环的各种阶段(macrotask卡塔 尔(英语:State of Qatar)之间都会试行microtask,事件循环的开头会先进行叁遍 microtask。

  .then(console.log)

    console.log('catch: ', err)

setImmediate

promise2 Promise { <pending> }

  resolve('success2')

    console.error('fail2: ', e)

})

const promise = Promise.resolve()

运维结果:

1

Promise.resolve()

once

end

    console.log(res)

nextTick

})

运作结果:

})

  reject('error')

setTimeout(() => {

题目六

姚丽冰    学号:16050120089

  .then((res) => {

    at <anonymous> }

1

setImmediate(() => {

运转结果:

    console.log('catch: ', err)

题目三

运营结果:

    return new Error('error!!!')

process.nextTick(() => {

  setTimeout(() => {

  })

  }, 1000)

2

const promise = new Promise((resolve, reject) => {

const promise = new Promise((resolve, reject) => {

  })

运维结果:

  .then(() => {

console.log('promise2', promise2)

  .then(function success (res) {

  console.log(1)

题目九

  console.log('tick')

    console.log('then: ', res)

Promise.resolve()

  })

    console.log(res)

TypeError: Chaining cycle detected for promise #<Promise>

    throw new Error('error')

  console.log('nextTick')

运转结果:

  })

Promise.resolve()

运作结果:

})

promise.then(() => {

})

3

promise

运转结果:

  console.log('setImmediate')

    console.log('once')

分解:promise 有 3 种状态:pending、fulfilled 或 rejected。状态更换只好是 pending->fulfilled 或许pending->rejected,状态大器晚成旦改动则不可能再变。下面 promise2 并不是promise1,而是再次来到的三个新的 Promise 实例。

    console.error('fail2: ', e)

success 1005

(node:50928) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

    at process._tickCallback (internal/process/next_tick.js:188:7)

  })

Promise.resolve(1)

  })

运维结果:

    return promise

题目八

console.log(4)

  })

    at ...

const start = Date.now()

因为再次回到任性叁个非 promise 的值都会棉被服装进成 promise 对象,即 return new Error('error!!!') 等价于 return Promise.resolve(new Error('error!!!'))。

  throw new Error('error!!!')

  .catch((err) => {

  .then(() => {

释疑:构造函数中的 resolve 或 reject 只有首先次执行有效,数十次调用未有其他成效,呼应代码二结论:promise 状态生机勃勃旦改动则不可能再变。

})

  })

then

return Promise.reject(new Error('error!!!'))

  console.log(res, Date.now() - start)

(node:50928) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: error!!!

  console.log(2)

  .catch(function fail2 (e) {

  <rejected> Error: error!!!

fail2: Error: error

}, 2000)

Promise.resolve()

console.log('end')

【嵌牛提问】:本文依据 Promise 的一些知识点总结了十道题,看看你能做对几道。

题目四

  process.nextTick(tick)

题目一

运维结果:

  }, 1000)

  .catch((err) => {

表明:.then 能够收起七个参数,第叁个是拍卖成功的函数,第二个是管理错误的函数。.catch 是 .then 第二个参数的省事写法,可是它们用法上有一些索要专心:.then 的第三个管理错误的函数捕获不了第贰个处理成功的函数抛出的失实,而持续的 .catch 可以捕获从前的不当。当然以下代码也足以:

  .then(function success1 (res) {

  .catch((err) => {

  .then(() => {

then: Error: error!!!

  .then(function success2 (res) {

    console.log('then: ', res)

  })

  })

    at <anonymous>

4

})

    at ...

    console.error('fail1: ', e)

  .then(2)

  })

    at promise.then (...)

  .then(Promise.resolve(3))

  }, function fail1 (e) {

process.nextTick(function tick () {

then: success1

    return 2

  .then((res) => {

原版的书文链接 zhuanlan.zhihu.com

  }, function fail2 (e) {

    resolve('success')

  })

  })

  console.log('promise1', promise1)

解释:promise 能够链式调用。聊起链式调用大家常常会想到通过 return this 完结,然则 Promise 而不是那样达成的。promise 每便调用 .then 也许 .catch 都会回来八个新的 promise,进而实现了链式调用。

    throw new Error('error')

【嵌牛导读】Promise 想必我们都不行熟练,想一想就那么几个api,可是你确实领会 Promise 吗?

    at success (...)

promise2 Promise {

  })

    at bootstrap_node.js:607:3

  }, function fail1 (e) {

【嵌牛鼻子】:Promise

  console.log(res, Date.now() - start)

const promise1 = new Promise((resolve, reject) => {

})

    at Promise.resolve.then (...)

    console.log('then')

讲解:Promise 构造函数是同台实施的,promise.then 中的函数是异步推行的。

表明:.then 也许 .catch 中 return 一个 error 对象并不会抛出怪诞,所以不会被三回九转的 .catch 捕获,需求改成在那之中生龙活虎种:

})

    resolve('success')

本文由金沙棋牌发布于金沙棋牌官方平台,转载请注明出处:流程控制,异步流程控制

关键词:

上一篇:没有了

下一篇:没有了